
using Autofac;
using LinqKit;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;

using DJY.Code;
using DJY.Code.Enums;
using DJY.Domain.Entity.SystemSecurity;
using DJY.Domain.Entity.SystemManage;
using DJY.Service.SystemSecurity;
using DJY.Application.SystemSecurity;
using DJY.Domain.ViewModel;
using DJY.Domain.ViewModel.WorkFlow;
using DJY.Service.WorkFlow;
using DJY.Domain.Entity.WorkFlow;
using DJY.Domain.IRepository.WorkFlow;
using System.Reflection;
using DJY.Code.Helper;
using System.Dynamic;
using System.Text;
using Newtonsoft.Json;

namespace DJY.Application.WorkFlow
{
    public class WorkFlowEngine : IWorkFlowEngine
    {
        static readonly string[] DeleteItemColumns = { "F_DeleteMark", "F_LastUpdateTime", "F_LastUpdateUser" };
        static readonly string[] PublishDefColumns = { "StableVersion", "F_LastUpdateTime", "F_LastUpdateUser" };
        static readonly string[] UpdateDefColumns = { "CateId", "Name", "Remark", "F_LastUpdateTime", "F_LastUpdateUser" };
        static readonly string[] UpdateDefVerColumns = { "Before", "After", "BusinessUrl", "AuditUrl", "ModelKey", "Size", "SkipEvent" };
        static readonly string[] VersionColumns = { "Version" };
        static readonly string[] UpdateDefNodeColumns = { "Name", "Remark", "Rectangle", "Comment", "Code", "Before",
            "After", "BusinessUrl", "AuditUrl", "ModelKey","F_LastUpdateTime","F_LastUpdateUser" };
        static readonly string[] UpdateFlowColumns = { "Remark", "F_LastUpdateTime", "F_LastUpdateUser", "NodeId" };

        static readonly string[] DeleteMsgColumns = { "Remark", "F_LastUpdateTime", "State" };
        static readonly string[] AddWeightColumns = { "Weight", "F_LastUpdateTime"};
        /// <summary>
        /// 事件方法参数
        /// </summary>
        static readonly Type[] BeforeEventArgs = { typeof(WorkFlowBeforeEventContext) };
        static readonly Type[] AfterEventArgs = { typeof(WorkFlowBeforeEventContext) };

        #region 流程分类
        /// <summary>
        /// 获取模块按钮权限
        /// </summary>
        /// <param name="userId">登陆用户</param>
        /// <returns></returns>
        public List<string> GetCateModuleButtons(string userId)
        {
            return SysService.GetUserModuleBtns(userId, Constants.WFCateModuleName);
        }

        /// <summary>
        /// 保存流程分类
        /// </summary>
        /// <param name="categroy">分类信息</param>
        /// <returns>操作结果</returns>
        public EditItemResult SaveCategory(WorkFlowCategory category)
        {
            if (category == null)
            {
                throw new ArgumentNullException(nameof(category));
            }

            if (category.id == 0)
            {
                return AddCategory(category);
            }
            else
            {
                return UpdateCategory(category);
            }
        }

        /// <summary>
        /// 保存流程分类
        /// </summary>
        /// <param name="ids">键列表</param>
        /// <param name="state">状态</param>
        /// <param name="userId">操作用户ID</param>
        /// <returns>操作结果</returns>
        public EditItemResult DeleteCategory(List<string> ids,bool state,string userId)
        {
            if (ids == null)
            {
                throw new ArgumentNullException(nameof(ids));
            }

            if (ids.Count == 0 || string.IsNullOrEmpty(userId))
            {
                return EditItemResult.Ok();
            }

            using (var scope = OperatorProvider.BeginLifetimeScope(null))
            {
                using (var db = scope.Resolve<DbContext>())
                {
                    IWFCategoryRepository repo = OperatorProvider.Resolve<IWFCategoryRepository>();

                    List<int> cids = ids.Select(id => int.Parse(id)).ToList();
                    List<WFCategoryEntity> list = db.Set<WFCategoryEntity>()
                    .Where(c => cids.Contains(c.Id) && c.F_DeleteMark != state)
                    .Select(c => new {
                        c.Id,
                        c.F_DeleteMark
                    })
                    .ToList()
                    .Select(c => new WFCategoryEntity() {
                        Id = c.Id,
                        F_DeleteMark = state,
                        F_LastUpdateTime = DateTime.Now,
                        F_LastUpdateUser = userId
                    }).ToList();

                    int count = 0;
                    if (list.Count > 0)
                    {
                        count = repo.Update(db, DeleteItemColumns, list);
                        DeleteChildCategory(list, state, userId, db, repo);
                    }

                    return EditItemResult.Ok(null, count.ToString());
                }
            }
        }

        /// <summary>
        /// 彻底删除分类
        /// </summary>
        /// <param name="ids">键列表</param>
        /// <param name="userId">操作用户ID</param>
        /// <returns>操作结果</returns>
        public EditItemResult RemoveCategory(List<string> ids, string userId)
        {
            if (ids == null)
            {
                throw new ArgumentNullException(nameof(ids));
            }

            if (ids.Count == 0 || string.IsNullOrEmpty(userId))
            {
                return EditItemResult.Ok();
            }

            using (var scope = OperatorProvider.BeginLifetimeScope(null))
            {
                using (var db = scope.Resolve<DbContext>())
                {

                    List<int> cids = ids.Select(id => int.Parse(id)).ToList();
                    int count = db.Set<WFCategoryEntity>().Count(c => cids.Contains(c.ParentId));
                    if (count > 0)
                    {
                        return EditItemResult.Fail("包含子级的项不能被删除");
                    }

                    var cate = db.Set<WFDefEntity>().Where(d => cids.Contains(d.CateId))
                    .Select(d => new { d.CateId }).FirstOrDefault();
                    if (cate != null)
                    {
                        return EditItemResult.Fail($"分类({cate.CateId})使用中，不能删除");
                    }

                    IWFCategoryRepository repo = OperatorProvider.Resolve<IWFCategoryRepository>();
                    count = repo.Delete(db, c => cids.Contains(c.Id));

                    return EditItemResult.Ok(null, count.ToString());
                }
            }
        }

        /// <summary>
        /// 流程分类查询
        /// </summary>
        /// <param name="queryInfo">分类查询信息</param>
        /// <returns>查询结果</returns>
        public PagedResult QueryCategory(WFCategoryQueryInfo queryInfo)
        {
            if (queryInfo == null)
            {
                queryInfo = new WFCategoryQueryInfo();
            }

            if (string.IsNullOrEmpty(queryInfo.userId))
            {
                return PagedResult.Empty;
            }

            int total = 0;
            List<WorkFlowCategory> rows = null;

            using (var scope = OperatorProvider.BeginLifetimeScope(null))
            {
                using (var db = scope.Resolve<DbContext>())
                {
                    IQueryable<WFCategoryEntity> query = db.Set<WFCategoryEntity>().Where(i=>i.F_OrganizeId == queryInfo.orgId);
                    if (!string.IsNullOrEmpty(queryInfo.code))
                    {
                        query = query.Where(i => i.Code == queryInfo.code);
                    }

                    if (!string.IsNullOrEmpty(queryInfo.name))
                    {
                        query = query.Where(i => i.Name.Contains(queryInfo.name));
                    }

                    if (!string.IsNullOrEmpty(queryInfo.remark))
                    {
                        query = query.Where(i => i.Remark != null && i.Remark.Contains(queryInfo.remark));
                    }

                    if (queryInfo.parentId.HasValue)
                    {
                        query = query.Where(i => i.ParentId == queryInfo.parentId.Value);
                    }

                    if (queryInfo.fDeleteMark.HasValue)
                    {
                        query = query.Where(i => i.F_DeleteMark == queryInfo.fDeleteMark.Value);
                    }

                    var search = query.LeftOuterJoin(db.Set<SysUserEntity>(), c => c.F_CreateUser, cu => cu.F_Id, (c, cu) => new
                    {
                        c.Id,
                        c.Code,
                        c.Name,
                        c.ParentId,
                        c.F_DeleteMark,
                        c.F_CreateTime,
                        c.F_CreateUser,
                        c.F_LastUpdateTime,
                        c.F_LastUpdateUser,
                        c.SortIdx,
                        CreateUserName = cu.F_RealName == null ? cu.F_NickName : cu.F_RealName
                    }).LeftOuterJoin(db.Set<WFCategoryEntity>(), c => c.ParentId, pc => pc.Id, (c, pc) => new {
                        c.Id,
                        c.Code,
                        c.Name,
                        c.ParentId,
                        ParentName = pc.Name,
                        c.F_DeleteMark,
                        c.F_CreateTime,
                        c.F_CreateUser,
                        c.F_LastUpdateTime,
                        c.F_LastUpdateUser,
                        c.SortIdx,
                        c.CreateUserName
                    });

                    total = search.Count();

                    rows = search.OrderBy(c => c.SortIdx).Skip((queryInfo.page - 1) * queryInfo.rows)
                    .Take(queryInfo.rows).Select(c => new WorkFlowCategory()
                    {
                        id = c.Id,
                        code = c.Code,
                        name = c.Name,
                        parentId = c.ParentId,
                        parentName = c.ParentName,
                        deleteMark = c.F_DeleteMark,
                        createTime = c.F_CreateTime.ToString("yyyy-MM-dd HH:mm:ss"),
                        createUser = c.CreateUserName,
                        lastUpdateTime = c.F_LastUpdateTime.HasValue ? c.F_LastUpdateTime.Value.ToString("yyyy-MM-dd HH:mm:ss") : null,
                        sortIdx = c.SortIdx
                    }).ToList();
                }
            }

            return new PagedResult() {
                records = total,
                total = (int)Math.Ceiling((float)total / queryInfo.rows),
                page = queryInfo.page,
                rows = rows
            };
        }

        /// <summary>
        /// 查询流程分类
        /// </summary>
        /// <param name="queryInfo">查询参数</param>
        /// <returns>查询结果</returns>
        public List<WorkFlowCategory> CategoryList(WFCategoryQueryInfo queryInfo)
        {
            if (queryInfo == null)
            {
                queryInfo = new WFCategoryQueryInfo();
            }

            using (var scope = OperatorProvider.BeginLifetimeScope(null))
            {
                using (var db = scope.Resolve<DbContext>())
                {
                    IQueryable<WFCategoryEntity> query = db.Set<WFCategoryEntity>();

                    if (queryInfo.id.HasValue)
                    {
                        query = query.Where(c => c.Id == queryInfo.id.Value);
                    }
                    else
                    {
                        query = query.Where(c => c.F_OrganizeId == queryInfo.orgId);
                    }

                    if (queryInfo.fDeleteMark.HasValue)
                    {
                        query = query.Where(c => c.F_DeleteMark == queryInfo.fDeleteMark.Value);
                    }

                    var search = query.LeftOuterJoin(db.Set<SysUserEntity>(), c => c.F_CreateUser, cu => cu.F_Id, (c, cu) => new
                    {
                        c.Id,
                        c.Code,
                        c.Name,
                        c.ParentId,
                        c.Level,
                        c.Remark,
                        c.F_DeleteMark,
                        c.F_CreateTime,
                        c.F_CreateUser,
                        c.F_LastUpdateTime,
                        c.F_LastUpdateUser,
                        c.SortIdx,
                        CreateUserName = cu.F_RealName == null ? cu.F_NickName : cu.F_RealName
                    }).LeftOuterJoin(db.Set<SysUserEntity>(), c => c.F_LastUpdateUser, uu => uu.F_Id, (c, uu) => new
                    {
                        c.Id,
                        c.Code,
                        c.Name,
                        c.ParentId,
                        c.Level,
                        c.Remark,
                        c.F_DeleteMark,
                        c.F_CreateTime,
                        c.F_CreateUser,
                        c.F_LastUpdateTime,
                        c.F_LastUpdateUser,
                        c.SortIdx,
                        c.CreateUserName,
                        LastUpdateUserName = uu.F_RealName == null ? uu.F_NickName : uu.F_RealName
                    }).LeftOuterJoin(db.Set<WFCategoryEntity>(), c => c.ParentId, pc => pc.Id, (c, pc) => new
                    {
                        c.Id,
                        c.Code,
                        c.Name,
                        c.ParentId,
                        c.Level,
                        c.Remark,
                        ParentName = pc.Name,
                        c.F_DeleteMark,
                        c.F_CreateTime,
                        c.F_CreateUser,
                        c.F_LastUpdateTime,
                        c.F_LastUpdateUser,
                        c.SortIdx,
                        c.CreateUserName,
                        c.LastUpdateUserName
                    });

                    return search.OrderBy(c => c.ParentId).OrderBy(c => c.SortIdx)
                        .ToList()
                        .Select(c => new WorkFlowCategory()
                        {
                            id = c.Id,
                            code = c.Code,
                            name = c.Name,
                            parentId = c.ParentId,
                            level = c.Level,
                            remark = c.Remark,
                            parentName = c.ParentName,
                            deleteMark = c.F_DeleteMark,
                            createTime = c.F_CreateTime.ToString("yyyy-MM-dd HH:mm:ss"),
                            createUser = c.CreateUserName,
                            lastUpdateTime = c.F_LastUpdateTime.HasValue ? c.F_LastUpdateTime.Value.ToString("yyyy-MM-dd HH:mm:ss") : null,
                            lastUpdateUser = c.LastUpdateUserName,
                            sortIdx = c.SortIdx
                        }).ToList();
                }
            }
        }

        #endregion

        #region 消息
        /// <summary>
        /// 我的待处理信息列表
        /// </summary>
        /// <param name="userId">操作人</param>
        /// <param name="state">状态</param>
        /// <param name="top">获取数据量</param>
        /// <returns></returns>
        public List<WfMyMsg> MyMsgList(string userId, int state = 0, int top = 15)
        {
            if (string.IsNullOrEmpty(userId))
            {
                return new List<WfMyMsg>();
            }

            using (var scope = OperatorProvider.BeginLifetimeScope(null))
            {
                using (var db = scope.Resolve<DbContext>())
                {
                    return db.Set<WFMessageEntity>()
                        .Where(m => m.OperateMan == userId && m.State == state)
                        .Join(db.Set<WFInstanceEntity>(), m => m.InstanceId, i => i.Id, (m, i) => new {
                            m.Id,
                            m.InstanceId,
                            m.NodeId,
                            m.OperateMan,
                            m.Msg,
                            m.F_LastUpdateTime,
                            m.State,
                            m.Remark,
                            m.Weight,
                            i.Name
                        }).LeftOuterJoin(db.Set<SysUserEntity>(), m => m.OperateMan, u => u.F_Id, (m, u) => new {
                            m.Id,
                            m.InstanceId,
                            m.NodeId,
                            OperateMan = u.F_RealName == null ? u.F_NickName : u.F_RealName,
                            m.Msg,
                            m.F_LastUpdateTime,
                            m.State,
                            m.Remark,
                            m.Weight,
                            m.Name
                        }).OrderByDescending(m => m.Weight).Take(top)
                        .ToList().Select(m => new WfMyMsg
                        {
                            id = m.Id.ToString(),
                            cate = m.Name,
                            operateMan = m.OperateMan,
                            time = m.F_LastUpdateTime?.ToString("yyyy-MM-dd hh:mm:ss"),
                            msg = m.Msg,
                            state = m.State,
                            remark = m.Remark,
                            weight = m.Weight
                        }).ToList();
                }
            }
        }

        /// <summary>
        /// 我的消息查询
        /// </summary>
        /// <param name="queryInfo">查询条件</param>
        /// <returns></returns>
        public PagedResult MyMsg(WFMessageQueryInfo queryInfo)
        {
            if (queryInfo == null)
            {
                throw new ArgumentNullException();
            }

            int total = 0;
            List<WfMyMsg> rows = null;
            if (string.IsNullOrEmpty(queryInfo.userId))
            {
                return new PagedResult() { total = total, rows = rows };
            }

            using (var scope = OperatorProvider.BeginLifetimeScope(null))
            {
                using (var db = scope.Resolve<DbContext>())
                {
                    IQueryable<WFMessageEntity> query = db.Set<WFMessageEntity>();
                    if (!queryInfo.isSys)
                    {
                        if (string.IsNullOrEmpty(queryInfo.operateMan))
                        {
                            queryInfo.operateMan = queryInfo.userId;
                        }
                    }

                    if (!string.IsNullOrEmpty(queryInfo.operateMan))
                    {
                        query = query.Where(m => m.OperateMan == queryInfo.operateMan);
                    }

                    if (!string.IsNullOrEmpty(queryInfo.instanceId))
                    {
                        query = query.Where(m => m.InstanceId == queryInfo.instanceId);
                    }

                    int state = 0;
                    if (!string.IsNullOrEmpty(queryInfo.state))
                    {
                        state = int.Parse(queryInfo.state);
                        query = query.Where(m => m.State == state);
                    }

                    int weight = 0;
                    if (!string.IsNullOrEmpty(queryInfo.weight))
                    {
                        weight = int.Parse(queryInfo.weight);
                        query = query.Where(m => m.Weight == weight);
                    }

                    if (!string.IsNullOrEmpty(queryInfo.msg))
                    {
                        query = query.Where(m => m.Msg.Contains(queryInfo.msg));
                    }

                    DateTime beginTime = DateTime.Now;
                    if (!string.IsNullOrEmpty(queryInfo.beginF_LastUpdateTime))
                    {
                        beginTime = DateTime.Parse(queryInfo.beginF_LastUpdateTime);
                        query = query.Where(m => m.F_LastUpdateTime >= beginTime);
                    }

                    DateTime endTime = DateTime.Now;
                    if (!string.IsNullOrEmpty(queryInfo.endF_LastUpdateTime))
                    {
                        endTime = DateTime.Parse(queryInfo.endF_LastUpdateTime).AddDays(1);
                        query = query.Where(m => m.F_LastUpdateTime <= endTime);
                    }

                    var search = query.Join(db.Set<WFInstanceEntity>(), m => m.InstanceId, i => i.Id, (m, i) => new
                    {
                        m.Id,
                        m.InstanceId,
                        m.NodeId,
                        m.OperateMan,
                        m.Msg,
                        m.F_LastUpdateTime,
                        m.State,
                        m.Remark,
                        m.Weight,
                        i.Name
                    }).LeftOuterJoin(db.Set<SysUserEntity>(), m => m.OperateMan, u => u.F_Id, (m, u) => new
                    {
                        m.Id,
                        m.InstanceId,
                        m.NodeId,
                        OperateMan = u.F_RealName == null ? u.F_NickName : u.F_RealName,
                        m.Msg,
                        m.F_LastUpdateTime,
                        m.State,
                        m.Remark,
                        m.Weight,
                        m.Name
                    });

                    total = search.Count();
                    rows = search.OrderByDescending(m => m.Weight).Skip((queryInfo.page - 1) * queryInfo.rows)
                        .Take(queryInfo.rows)
                        .ToList().Select(m => new WfMyMsg
                        {
                            id = m.Id.ToString(),
                            cate = m.Name,
                            operateMan = m.OperateMan,
                            time = m.F_LastUpdateTime?.ToString("yyyy-MM-dd hh:mm:ss"),
                            msg = m.Msg,
                            state = m.State,
                            remark = m.Remark,
                            weight = m.Weight
                        }).ToList();
                }
            }

            return new PagedResult()
            {
                records = total,
                total = (int)Math.Ceiling((float)total / queryInfo.rows),
                page = queryInfo.page,
                rows = rows
            };
        }

        /// <summary>
        /// 处理待办
        /// </summary>
        /// <param name="id">消息ID</param>
        /// <param name="userId">当前处理人ID</param>
        /// <returns>失败,提示消息；成功，跳转到逻辑处理页面</returns>
        public ProcessMsgResult ProcessMsg(string id, string userId)
        {
            if (string.IsNullOrEmpty(id) || string.IsNullOrEmpty(userId))
            {
                throw new ArgumentNullException();
            }

            using (var scope = OperatorProvider.BeginLifetimeScope(null))
            {
                using (var db = scope.Resolve<DbContext>())
                {
                    WFMessageEntity msg = db.Set<WFMessageEntity>().Where(m => m.Id == id && m.OperateMan == userId).FirstOrDefault();
                    if (msg == null)
                    {
                        return ProcessMsgResult.Fail("待办事项不存在");
                    }

                    if (msg.State == 1)
                    {
                        return ProcessMsgResult.Fail("待办事项已处理完成");
                    }

                    if (msg.State == 2)
                    {
                        return ProcessMsgResult.Fail("待办事项已无效");
                    }

                    WFInstanceEntity flow = db.Set<WFInstanceEntity>().Where(i => i.Id == msg.InstanceId).FirstOrDefault();
                    if(flow == null || string.IsNullOrEmpty(flow.NodeId))
                    {
                        return ProcessMsgResult.Fail("待办事项无需处理");
                    }

                    WFVersionEntity flowVer = db.Set<WFVersionEntity>().Where(v => v.WFID == flow.WFID && v.Version == flow.Version).FirstOrDefault();
                    if(flowVer == null)
                    {
                        return ProcessMsgResult.Fail("待办事项无需处理");
                    }

                    string[] nodeIds = flow.NodeId.Split(',');
                    if(!nodeIds.Contains(msg.NodeId))
                    {
                        return ProcessMsgResult.Fail("待办事项无需处理");
                    }

                    string fromNode = null;
                    string defNode = msg.NodeId;
                    int idx = defNode.IndexOf(":");
                    if (idx > -1)
                    {
                        fromNode = defNode.Substring(0, idx);
                        defNode = defNode.Substring(idx + 1);
                    }

                    long? fromNodeId = null;
                    WFInstanceNodeEntity flowNode = null;
                    if (!string.IsNullOrEmpty(fromNode))
                    {
                        fromNodeId = long.Parse(fromNode);
                        flowNode = db.Set<WFInstanceNodeEntity>().Where(fn => fn.Id == fromNodeId.Value).FirstOrDefault();
                    }

                    WorkFlowNode to = db.Set<WFNodeEntity>().Where(n => n.Id == defNode)
                        .Join(db.Set<WFVersionEntity>(), n => new WorkFlowVersionKey { Id = n.WFID, Version = n.Version },
                        v => new WorkFlowVersionKey { Id = v.WFID, Version = v.Version }, (n, v) => new WorkFlowNode
                        {
                            id = n.Id,
                            code = n.Code,
                            type = n.NodeType,
                            name = n.Name,
                            remark = n.Remark,
                            rectangle = n.Rectangle,
                            comment = n.Comment,
                            businessUrl = n.NodeType == (int)WFNodeType.End ? null :(n.BusinessUrl == null || n.BusinessUrl == "" ? v.BusinessUrl : n.BusinessUrl),
                            auditUrl = (n.NodeType == (int)WFNodeType.Start || n.NodeType == (int)WFNodeType.End) ? null : (n.AuditUrl == null || n.AuditUrl == "" ? v.AuditUrl : n.AuditUrl),
                            modelKey = n.ModelKey == null || n.ModelKey == "" ? v.ModelKey : n.ModelKey,
                            before = n.Before,
                            after = n.After,
                            size = v.Size
                        }).FirstOrDefault();

                    if (to == null)
                    {
                        return ProcessMsgResult.Fail("操作节点不存在");
                    }

                    if (string.IsNullOrEmpty(to.businessUrl)
                        && string.IsNullOrEmpty(to.auditUrl))
                    {
                        DbContextTransaction trans = db.Database.BeginTransaction();
                        try
                        {
                            WorkFlowBeforeEventContext context = new WorkFlowBeforeEventContext();
                            context.dao = db;
                            context.after = flowVer.After;
                            context.instance = flow;
                            context.userId = userId;
                            WorkFlowInstanceNode from = null;
                            if (flowNode != null)
                            {
                                from = new WorkFlowInstanceNode();
                                from.instanceId = flowNode.InstanceId;
                                from.createTime = flowNode.F_CreateTime.ToString("yyyy-MM-dd HH:mm:ss");
                                from.createUser = flowNode.F_CreateUser;
                                from.fromNode = flowNode.FromNode?.ToString();
                                from.fromNodeName = flowNode.FromNodeName;
                                from.nodeId = flowNode.NodeId;
                                from.nodeType = flowNode.NodeType;
                                from.nodeName = context.to.name;
                                from.modelKey = context.to.modelKey;
                                from.ID = flowNode.Id;
                                from.id = flowNode.Id.ToString();
                            }
                            context.from = from;
                            context.to = to;
                            StringBuilder path = new StringBuilder();
                            Next(context, path);
                            if (context.cancel)
                            {
                                trans.Rollback();
                                return ProcessMsgResult.Fail(string.IsNullOrEmpty(context.message) ? "操作被取消" : context.message);
                            }

                            if (context.error != null)
                            {
                                trans.Rollback();
                                return ProcessMsgResult.Fail(string.IsNullOrEmpty(context.message) ? Common.ShowException(context.error) : context.message);
                            }
                            trans.Commit();
                            return ProcessMsgResult.Fail("处理完成");
                        }
                        catch (Exception ex)
                        {
                            try
                            {
                                trans.Rollback();
                            }
                            catch
                            {
                                //回滚失败
                            }
                            return ProcessMsgResult.Fail(Common.ShowException(ex));
                        }
                    }
                    else
                    {
                        if (string.IsNullOrEmpty(to.name))
                        {
                            to.name = to.type == (int)WFNodeType.Start ? "开始流程" : "待处理";
                        }

                        return ProcessMsgResult.Ok(to, $"{msg.InstanceId},{msg.NodeId}");
                    }
                }
            }
        }

        /// <summary>
        /// 催办
        /// </summary>
        /// <param name="id">消息ID</param>
        /// <param name="userId">当前处理人ID</param>
        /// <returns></returns>
        public EditItemResult AddWeight(string id, string userId)
        {
            if (string.IsNullOrEmpty(id) || string.IsNullOrEmpty(userId))
            {
                throw new ArgumentNullException();
            }

            using (var scope = OperatorProvider.BeginLifetimeScope(null))
            {
                using (var db = scope.Resolve<DbContext>())
                {
                    List<WFMessageEntity> msgs = db.Set<WFMessageEntity>().Where(m => m.InstanceId == id 
                    && m.State == 0 && m.Weight<2).ToList();
                    if (msgs.Count > 0)
                    {
                        msgs.ForEach(m =>
                        {
                            m.Weight++;
                            m.F_LastUpdateTime = DateTime.Now;
                        });

                        DbContextTransaction trans = db.Database.BeginTransaction();

                        IWFMessageRepository msgRepos = OperatorProvider.Resolve<IWFMessageRepository>();
                        int offset = 0;
                        while (offset < msgs.Count)
                        {
                            msgRepos.Update(db, AddWeightColumns, msgs.Skip(offset).Take(500).ToList());
                            offset += 500;
                        }

                        trans.Commit();
                    }

                    return EditItemResult.Ok();
                }
            }
        }
        #endregion

        #region 流程定义
        /// <summary>
        /// 获取模块按钮权限
        /// </summary>
        /// <param name="userId">登陆用户</param>
        /// <returns></returns>
        public List<string> GetDefModuleButtons(string userId)
        {
            return SysService.GetUserModuleBtns(userId, Constants.WFDefModuleName);
        }

        /// <summary>
        /// 流程定义属性列表
        /// </summary>
        public List<WFDefTreeModel> WfDefTreeList(string orgId)
        {
            using (var scope = OperatorProvider.BeginLifetimeScope(null))
            {
                using (var db = scope.Resolve<DbContext>())
                {
                    return db.Set<WFDefEntity>().Where(f => !f.F_DeleteMark && f.F_OrganizeId == orgId)
                        .Join(db.Set<WFCategoryEntity>(), f => f.CateId, c => c.Id, (f, c) => new
                        {
                            f.Id,
                            f.Name,
                            f.CateId,
                            CateName = c.Name,
                            c.ParentId
                        }).LeftOuterJoin(db.Set<WFCategoryEntity>(), f => f.ParentId, pc => pc.Id, (f, pc) => new
                        {
                            f.Id,
                            f.Name,
                            f.CateId,
                            f.CateName,
                            f.ParentId,
                            ParentName = pc.Name
                        }).ToList()
                        .Select(f => new WFDefTreeModel()
                        {
                            wfId = f.Id,
                            name = f.Name,
                            cateId = f.CateId.ToString(),
                            cateName = f.CateName,
                            parentId = f.ParentId.ToString(),
                            parentName = f.ParentName
                        }).ToList();
                }
            }
        }

        /// <summary>
        /// 我的流程列表
        /// </summary>
        /// <param name="userId">用户ID</param>
        public List<MyFlowModel> MyFlowList(string userId)
        {
            if (string.IsNullOrEmpty(userId))
            {
                return new List<MyFlowModel>();
            }

            using (var scope = OperatorProvider.BeginLifetimeScope(null))
            {
                using (var db = scope.Resolve<DbContext>())
                {
                    return db.Set<SysUserEntity>().Where(u => u.F_Id == userId)
                        .Join(db.Set<SysRoleEntity>(), u => u.F_RoleId, r => r.F_Id, (u, r) => new
                        {
                            r.F_Id
                        }).Join(db.Set<SysRoleAuthorizeEntity>().Where(a => a.F_ItemType == 4),
                        u => u.F_Id, a => a.F_ObjectId, (u, a) => new
                        {
                            a.F_ItemId
                        }).Join(db.Set<WFDefEntity>(), u => u.F_ItemId, d => d.Id, (u, d) => new
                        {
                            d.Id,
                            d.CateId,
                            d.Name,
                            d.Version,
                            d.Remark,
                            d.StableVersion,
                            d.F_CreateUser,
                            d.F_CreateTime,
                            d.F_DeleteMark
                        }).Join(db.Set<WFCategoryEntity>(), d => d.CateId, c => c.Id, (d, c) => new
                        {
                            d.Id,
                            d.CateId,
                            CateName = c.Name,
                            c.ParentId,
                            d.Name,
                            d.Version,
                            d.Remark,
                            d.StableVersion,
                            d.F_CreateUser,
                            d.F_CreateTime,
                            d.F_DeleteMark
                        }).Join(db.Set<WFVersionEntity>(), d => new WorkFlowVersionKey
                        {
                            Id = d.Id,
                            Version = d.Version
                        }, v => new WorkFlowVersionKey
                        {
                            Id = v.WFID,
                            Version = v.Version
                        }, (d, v) => new
                        {
                            d.Id,
                            d.CateId,
                            d.CateName,
                            d.ParentId,
                            d.Name,
                            d.Version,
                            d.Remark,
                            d.StableVersion,
                            d.F_CreateUser,
                            d.F_CreateTime,
                            d.F_DeleteMark,
                            v.BusinessUrl,
                            v.AuditUrl,
                            v.Before,
                            v.After,
                            v.ModelKey
                        }).LeftOuterJoin(db.Set<WFCategoryEntity>(), d => d.ParentId, pc => pc.Id, (d, pc) => new
                        {
                            d.Id,
                            d.CateId,
                            CateName = pc.Name == null ? d.CateName : (pc.Name + "-" + d.CateName),
                            d.Name,
                            d.Version,
                            d.Remark,
                            d.StableVersion,
                            d.F_CreateUser,
                            d.F_CreateTime,
                            d.F_DeleteMark,
                            d.BusinessUrl,
                            d.AuditUrl,
                            d.Before,
                            d.After,
                            d.ModelKey
                        }).LeftOuterJoin(db.Set<SysUserEntity>(), d => d.F_CreateUser, cu => cu.F_Id, (d, cu) => new
                        {
                            d.Id,
                            d.CateId,
                            d.CateName,
                            d.Name,
                            d.Version,
                            d.Remark,
                            d.StableVersion,
                            F_CreateUser = cu.F_RealName == null ? cu.F_NickName : cu.F_RealName,
                            d.F_CreateTime,
                            d.F_DeleteMark,
                            d.BusinessUrl,
                            d.AuditUrl,
                            d.Before,
                            d.After,
                            d.ModelKey
                        }).ToList().GroupBy(d => new { d.CateId, d.CateName })
                        .Select(g => new MyFlowModel
                        {
                            cateId = g.Key.CateId.ToString(),
                            cateName = g.Key.CateName,
                            defs = g.Select(d => new MyFlowDefModel
                            {
                                id = d.Id,
                                name = d.Name,
                                version = d.Version,
                                stableVersion = d.StableVersion,
                                businessUrl = d.BusinessUrl,
                                auditUrl = d.AuditUrl,
                                modelKey = d.ModelKey,
                                before = d.Before,
                                after = d.After,
                                remark = d.Remark,
                                createTime = d.F_CreateTime.ToString("yyyy-MM-dd HH:mm:ss"),
                                createUser = d.F_CreateUser,
                                deleteMark = d.F_DeleteMark
                            }).ToList()
                        }).ToList();
                }
            }
        }

        /// <summary>
        /// 发布流程
        /// </summary>
        /// <param name="ids">流程id</param>
        /// <param name="userId">操作用户</param>
        /// <returns></returns>
        public EditItemResult Publish(List<string> ids, string userId)
        {
            if (ids == null || ids.Count == 0)
            {
                return EditItemResult.Ok(null, null);
            }

            using (var scope = OperatorProvider.BeginLifetimeScope(null))
            {
                using (var db = scope.Resolve<DbContext>())
                {
                    List<WFDefEntity> list = db.Set<WFDefEntity>()
                        .Where(d => ids.Contains(d.Id) && d.Version != d.StableVersion)
                        .ToList();

                    if (list.Count == 0)
                    {
                        return EditItemResult.Ok(null, null);
                    }

                    EditItemResult result = CheckFlowDef(db, ids);
                    if (result != null)
                    {
                        return result;
                    }

                    list.ForEach(def => {
                        def.StableVersion = def.Version;
                        def.F_LastUpdateTime = DateTime.Now;
                        def.F_LastUpdateUser = userId;
                    });

                    IWFDefRepository defRepos = OperatorProvider.Resolve<IWFDefRepository>();
                    int count = defRepos.Update(db, PublishDefColumns, list);
                    return EditItemResult.Ok(null, count.ToString());
                }
            }
        }

        /// <summary>
        /// 检查流程配置事件和模型类是否有效
        /// </summary>
        /// <param name="db">数据库实例</param>
        /// <param name="ids">定义ID</param>
        /// <returns></returns>
        public EditItemResult CheckFlowDef(DbContext db, List<string> ids)
        {
            var wfdefs = db.Set<WFDefEntity>().Where(d => ids.Contains(d.Id))
                .Join(db.Set<WFVersionEntity>(), d => new WorkFlowVersionKey { Id = d.Id, Version = d.Version },
                v => new WorkFlowVersionKey { Id = v.WFID, Version = v.Version }, (d, v) => new
                {
                    Type = 1,
                    d.Name,
                    NodeName = "",
                    v.BusinessUrl,
                    v.AuditUrl,
                    v.ModelKey,
                    v.After,
                    v.Before,
                    v.SkipEvent
                }).Union(db.Set<WFDefEntity>().Where(d => ids.Contains(d.Id))
                .Join(db.Set<WFNodeEntity>().Where(n => n.NodeType != 0), d => new WorkFlowVersionKey { Id = d.Id, Version = d.Version },
                n => new WorkFlowVersionKey { Id = n.WFID, Version = n.Version }, (d, n) => new
                {
                    Type = 2,
                    d.Name,
                    NodeName = n.Name,
                    n.BusinessUrl,
                    n.AuditUrl,
                    n.ModelKey,
                    n.After,
                    n.Before,
                    SkipEvent = ""
                })).ToList();

            List<string> validKeys = new List<string>();
            foreach (var def in wfdefs)
            {
                string name = $"流程({def.Name})", key = def.ModelKey, ckey = null;
                if (def.Type == 2)
                {
                    name += $"节点({def.NodeName})";
                }

                #region 模型类
                if (!string.IsNullOrEmpty(key))
                {
                    key = key.Trim().Replace(" ", "");
                    if (key.IndexOf('.') == -1)
                    {
                        key = "DJY.Domain.ViewModel." + key;
                    }

                    if (key.IndexOf(",") == -1)
                    {
                        key += ",DJY.Domain";
                    }

                    ckey = "mk:" + key;
                    if (!validKeys.Contains(ckey))
                    {
                        try
                        {
                            Type type = Type.GetType(key);
                            if (type == null)
                            {
                                return EditItemResult.Fail($"{name}未找到类型({def.ModelKey})");
                            }
                        }
                        catch
                        {
                            return EditItemResult.Fail($"未找到类型({def.ModelKey})");
                        }

                        validKeys.Add(ckey);
                    }
                }
                #endregion

                #region 事件
                key = def.SkipEvent;
                if (!string.IsNullOrEmpty(key))
                {
                    EditItemResult result = CheckFlowDefEvent(validKeys, name, "se:", "忽略", key, BeforeEventArgs);
                    if (result != null)
                    {
                        return result;
                    }
                }

                key = def.Before;
                if (!string.IsNullOrEmpty(key))
                {
                    EditItemResult result = CheckFlowDefEvent(validKeys, name, "be:", "开始", key, BeforeEventArgs);
                    if (result != null)
                    {
                        return result;
                    }
                }

                key = def.After;
                if (!string.IsNullOrEmpty(key))
                {
                    EditItemResult result = CheckFlowDefEvent(validKeys, name, "ae:", "结束", key, AfterEventArgs);
                    if (result != null)
                    {
                        return result;
                    }
                }
                #endregion
            }
            return null;
        }

        #endregion

        /// <summary>
        /// 查询用户
        /// </summary>
        /// <param name="userId">当前登陆用户</param>
        /// <param name="kws">查询关键字</param>
        /// <param name="type">查询类型</param>
        /// <returns></returns>
        public Dictionary<string, string> SearchUser(string userId,
            string kws,
            string type)
        {
            if (string.IsNullOrEmpty(userId))
            {
                return new Dictionary<string, string>();
            }

            using (var scope = OperatorProvider.BeginLifetimeScope(null))
            {
                using (var db = scope.Resolve<DbContext>())
                {
                    var user = db.Set<SysUserEntity>().Where(u => u.F_Id == userId).Select(u => new
                    {
                        u.F_OrganizeId
                    }).FirstOrDefault();

                    if (user == null)
                    {
                        return new Dictionary<string, string>();
                    }

                    switch (type)
                    {
                        case "r":
                            IQueryable<SysRoleEntity> roleQuery = db.Set<SysRoleEntity>().Where(r => r.F_CategoryId == 1 
                            && r.F_OrganizeId == user.F_OrganizeId && r.F_EnabledMark);
                            if (!string.IsNullOrEmpty(kws))
                            {
                                roleQuery = roleQuery.Where(r => r.F_FullName.Contains(kws) || r.F_EnCode == kws);
                            }
                            return roleQuery.OrderBy(r => r.F_SortCode).Select(r => new { r.F_Id, r.F_FullName })
                                .Take(100).ToList()
                                .ToDictionary(r => r.F_Id, r => r.F_FullName);
                        case "d":
                            IQueryable<SysOrganizeEntity> depQuery = db.Set<SysOrganizeEntity>().Where(r => r.F_ParentId == user.F_OrganizeId
                            && r.F_CategoryId == 2 && r.F_EnabledMark);
                            if (!string.IsNullOrEmpty(kws))
                            {
                                depQuery = depQuery.Where(r => r.F_FullName.Contains(kws) || r.F_EnCode == kws);
                            }
                            return depQuery.OrderBy(r => r.F_SortCode).Select(r => new { r.F_Id, r.F_FullName })
                                .Take(100).ToList()
                                .ToDictionary(r => r.F_Id, r => r.F_FullName);
                        case "p":
                            IQueryable<SysRoleEntity> dutyQuery = db.Set<SysRoleEntity>().Where(p => p.F_CategoryId == 2 
                            && p.F_OrganizeId == user.F_OrganizeId && p.F_EnabledMark);
                            if (!string.IsNullOrEmpty(kws))
                            {
                                dutyQuery = dutyQuery.Where(p => p.F_FullName.Contains(kws) || p.F_EnCode == kws);
                            }
                            return dutyQuery.OrderBy(r => r.F_SortCode).Select(r => new { r.F_Id, r.F_FullName })
                                .Take(100).ToList()
                                .ToDictionary(r => r.F_Id, r => r.F_FullName);
                        default:
                            IQueryable<SysUserEntity> userQuery = db.Set<SysUserEntity>().Where(r => r.F_OrganizeId == user.F_OrganizeId
                            && r.F_EnabledMark);
                            if (!string.IsNullOrEmpty(kws))
                            {
                                userQuery = userQuery.Where(r => (r.F_RealName != null && r.F_RealName.Contains(kws))
                                || (r.F_NickName != null && r.F_NickName.Contains(kws))
                                || r.F_MobilePhone == kws);
                            }
                            return userQuery.OrderBy(r => r.F_SortCode).Select(r => new { r.F_Id, F_FullName = r.F_RealName == null ? r.F_NickName : r.F_RealName })
                                .Take(100).ToList()
                                .ToDictionary(r => r.F_Id, r => r.F_FullName);
                    }
                }
            }
        }

        /// <summary>
        /// 获取节点用户ID
        /// </summary>
        /// <param name="db">数据库连接</param>
        /// <param name="orgs">组织ID,部门ID，岗位ID,角色ID</param>
        /// <param name="users">所有用户</param>
        /// <param name="isCandidate">是否候选</param>
        /// <param name="allUsers">类型所有用户</param>
        /// <returns></returns>
        public List<string> GetNodeUserIds(DbContext db,string[] orgs,List<WorkFlowNodeUser> users,
            bool isCandidate,Dictionary<int,List<string>> allUsers)
        {
            List<string> userIds = null;
            WorkFlowNodeUser allUser = users.FirstOrDefault(u => u.isCandidate == isCandidate && u.userId == "all");
            if (allUser != null)
            {
                if (!allUsers.ContainsKey(allUser.userType))
                {
                    allUsers[allUser.userType] = GetAllUser(db, allUser.userType, orgs);
                }

                userIds = allUsers[allUser.userType];
            }
            else
            {
                userIds = users.Where(u => u.userType == 1 && u.isCandidate == isCandidate).Select(u => u.userId).ToList();
                List<string> tids = users.Where(u => u.userType == 2 && u.isCandidate == isCandidate).Select(u => u.userId).ToList();
                if (tids.Count > 0)
                {
                    tids = GetAllUser(db, 2, orgs, tids);
                    userIds.AddRange(tids);
                }
                tids = users.Where(u => u.userType == 3 && u.isCandidate == isCandidate).Select(u => u.userId).ToList();
                if (tids.Count > 0)
                {
                    tids = GetAllUser(db, 3, orgs, tids);
                    userIds.AddRange(tids);
                }
                tids = users.Where(u => u.userType == 4 && u.isCandidate == isCandidate).Select(u => u.userId).ToList();
                if (tids.Count > 0)
                {
                    tids = GetAllUser(db, 4, orgs, tids);
                    userIds.AddRange(tids);
                }

                userIds = userIds.Distinct().ToList();
            }

            return userIds;
        }

        /// <summary>
        /// 查询用户
        /// </summary>
        /// <param name="queryInfo">查询条件</param>
        /// <returns></returns>
        public List<FlowUserModel> SearchFlowUser(FlowWFMessageQueryInfo queryInfo)
        {
            if(queryInfo == null || string.IsNullOrEmpty(queryInfo.userId))
            {
                return new List<FlowUserModel>();
            }

            using (var scope = OperatorProvider.BeginLifetimeScope(null))
            {
                using (var db = scope.Resolve<DbContext>())
                {
                    string orgId = db.Set<SysUserEntity>().Where(u => u.F_Id == queryInfo.userId).Select(u => u.F_OrganizeId).FirstOrDefault();
                    if(string.IsNullOrEmpty(orgId))
                    {
                        return new List<FlowUserModel>();
                    }

                    var search = db.Set<SysUserEntity>().Where(u => u.F_OrganizeId == orgId && u.F_EnabledMark)
                        .Join(db.Set<SysRoleEntity>(), u => u.F_RoleId, r => r.F_Id, (u, r) => new
                        {
                            UserId = u.F_Id,
                            UserName = u.F_RealName == null ? u.F_NickName : u.F_RealName,
                            RoleId = r.F_Id,
                            Role = r.F_FullName,
                            u.F_DepartmentId,
                            u.F_DutyId,
                            u.F_SortCode,
                            u.F_MobilePhone
                        }).Join(db.Set<SysOrganizeEntity>().Where(d => d.F_ParentId == orgId && d.F_CategoryId == 2), u => u.F_DepartmentId, d => d.F_Id, (u, d) => new
                        {
                            u.UserId,
                            u.UserName,
                            u.RoleId,
                            u.Role,
                            DepartId = d.F_Id,
                            Depart = d.F_FullName,
                            u.F_DutyId,
                            u.F_SortCode,
                            u.F_MobilePhone
                        }).Join(db.Set<SysRoleEntity>().Where(p => p.F_CategoryId == 2), u => u.F_DutyId, p => p.F_Id, (u, p) => new
                        {
                            u.UserId,
                            u.UserName,
                            u.RoleId,
                            u.Role,
                            u.DepartId,
                            u.Depart,
                            PostId = p.F_Id,
                            Post = p.F_FullName,
                            u.F_SortCode,
                            u.F_MobilePhone
                        });

                    if(!string.IsNullOrEmpty(queryInfo.role))
                    {
                        search = search.Where(u => u.RoleId == queryInfo.role);
                    }

                    if (!string.IsNullOrEmpty(queryInfo.depart))
                    {
                        search = search.Where(u => u.DepartId == queryInfo.depart);
                    }

                    if (!string.IsNullOrEmpty(queryInfo.post))
                    {
                        search = search.Where(u => u.PostId == queryInfo.post);
                    }

                    if (!string.IsNullOrEmpty(queryInfo.kws))
                    {
                        search = search.Where(u => u.UserName.Contains(queryInfo.kws) 
                        || u.F_MobilePhone == queryInfo.kws);
                    }

                    return search.OrderBy(u => u.F_SortCode).Take(100)
                        .Select(u => new FlowUserModel() { 
                            userId = u.UserId,
                            user = u.UserName,
                            role = u.Role,
                            roleId = u.RoleId,
                            depart = u.Depart,
                            departId = u.DepartId,
                            post = u.Post,
                            postId = u.PostId
                        }).ToList();

                }
            }
        }

        /// <summary>
        /// 保存流程
        /// </summary>
        /// <param name="def">流程定义</param>
        public EditItemResult Save(WorkFlowDef def)
        {
            using (var scope = OperatorProvider.BeginLifetimeScope(null))
            {
                using (var db = scope.Resolve<DbContext>())
                {
                    EditItemResult result = CheckWorkFlowDef(db, def);
                    if (result != null)
                    {
                        return result;
                    }

                    try
                    {
                        if(string.IsNullOrEmpty(def.id))
                        {
                            return AddWorkFlowDef(db,def);
                        }
                        else
                        {
                            return UpdateWorkFlowDef(db, def);
                        }
                    }
                    catch (Exception ex)
                    {
                        return EditItemResult.Fail((string.IsNullOrEmpty(def.id) ? "创建流程" : $"更新流程({def.id})") + "失败：" + Common.ShowException(ex));
                    }
                }
            }
        }

        /// <summary>
        /// 设置流程状态
        /// </summary>
        /// <param name="ids">键列表</param>
        /// <param name="state">状态</param>
        /// <param name="userId">操作用户ID</param>
        /// <returns>操作结果</returns>
        public EditItemResult DeleteFlow(List<string> ids, bool state, string userId)
        {
            if (ids == null)
            {
                throw new ArgumentNullException(nameof(ids));
            }

            if (ids.Count == 0 || string.IsNullOrEmpty(userId))
            {
                return EditItemResult.Ok();
            }

            using (var scope = OperatorProvider.BeginLifetimeScope(null))
            {
                using (var db = scope.Resolve<DbContext>())
                {
                    IWFDefRepository repo = OperatorProvider.Resolve<IWFDefRepository>();

                    List<WFDefEntity> list = db.Set<WFDefEntity>()
                    .Where(c => ids.Contains(c.Id) && c.F_DeleteMark != state)
                    .Select(c => new {
                        c.Id,
                        c.F_DeleteMark
                    })
                    .ToList()
                    .Select(c => new WFDefEntity()
                    {
                        Id = c.Id,
                        F_DeleteMark = state,
                        F_LastUpdateTime = DateTime.Now,
                        F_LastUpdateUser = userId
                    }).ToList();

                    int count = 0;
                    if (list.Count > 0)
                    {
                        count = repo.Update(db, DeleteItemColumns, list);
                    }

                    return EditItemResult.Ok(null, count.ToString());
                }
            }
        }

        /// <summary>
        /// 彻底删除流程
        /// </summary>
        /// <param name="ids">键列表</param>
        /// <param name="userId">操作用户ID</param>
        /// <returns>操作结果</returns>
        public EditItemResult RemoveFlow(List<string> ids, string userId)
        {
            if (ids == null)
            {
                throw new ArgumentNullException(nameof(ids));
            }

            if (ids.Count == 0 || string.IsNullOrEmpty(userId))
            {
                return EditItemResult.Ok();
            }

            using (var scope = OperatorProvider.BeginLifetimeScope(null))
            {
                using (var db = scope.Resolve<DbContext>())
                {
                    int count = db.Set<WFInstanceEntity>().Count(c => ids.Contains(c.WFID));
                    if (count > 0)
                    {
                        return EditItemResult.Fail("使用中的流程不能删除");
                    }

                    DbContextTransaction trans = db.Database.BeginTransaction();
                    try
                    {
                        IWFDefRepository wfRepo = OperatorProvider.Resolve<IWFDefRepository>();
                        count = wfRepo.Delete(db, d => ids.Contains(d.Id));

                        IWFVersionRepository verRepo = OperatorProvider.Resolve<IWFVersionRepository>();
                        count = verRepo.Delete(db, v => ids.Contains(v.WFID));

                        IWFNodeRepository nRepo = OperatorProvider.Resolve<IWFNodeRepository>();
                        nRepo.Delete(db, d => ids.Contains(d.WFID));

                        IWFNodeLinkRepository nlRepo = OperatorProvider.Resolve<IWFNodeLinkRepository>();
                        nlRepo.Delete(db, d => ids.Contains(d.WFID));

                        IWFNodeUserRepository nuRepo = OperatorProvider.Resolve<IWFNodeUserRepository>();
                        nuRepo.Delete(db, d => ids.Contains(d.WFID));

                        IWFNodePropertyRepository npRepo = OperatorProvider.Resolve<IWFNodePropertyRepository>();
                        npRepo.Delete(db, d => ids.Contains(d.WFID));

                        trans.Commit();
                        return EditItemResult.Ok(null, count.ToString());
                    }
                    catch (Exception ex)
                    {
                        trans.Rollback();
                        return EditItemResult.Fail(Common.ShowException(ex));
                    }

                }
            }
        }

        /// <summary>
        /// 流程定义查询
        /// </summary>
        /// <param name="queryInfo">流程查询信息</param>
        /// <returns>查询结果</returns>
        public List<WorkFlowDef> QueryFlow(WFDefQueryInfo queryInfo)
        {
            if (queryInfo == null)
            {
                queryInfo = new WFDefQueryInfo();
            }

            using (var scope = OperatorProvider.BeginLifetimeScope(null))
            {
                using (var db = scope.Resolve<DbContext>())
                {
                    IQueryable<WFDefEntity> query = db.Set<WFDefEntity>().Where(d=>d.F_OrganizeId == queryInfo.orgId);
                    if (queryInfo.fDeleteMark.HasValue)
                    {
                        query = query.Where(d => d.F_DeleteMark == queryInfo.fDeleteMark.Value);
                    }

                    if (!string.IsNullOrEmpty(queryInfo.id))
                    {
                        query = query.Where(d => d.Id == queryInfo.id);
                    }

                    if (queryInfo.cateId.HasValue && queryInfo.cateId.Value>0)
                    {
                        query = query.Where(d => d.CateId == queryInfo.cateId.Value);
                    }

                    if (!string.IsNullOrEmpty(queryInfo.name))
                    {
                        query = query.Where(d => d.Name.Contains(queryInfo.name));
                    }

                    if (!string.IsNullOrEmpty(queryInfo.remark))
                    {
                        query = query.Where(d => d.Remark != null && d.Remark.Contains(queryInfo.remark));
                    }

                    var search = query.LeftOuterJoin(db.Set<SysUserEntity>(), d => d.F_CreateUser, cu => cu.F_Id, (d, cu) => new
                    {
                        d.Id,
                        d.CateId,
                        d.Name,
                        d.Remark,
                        d.Version,
                        BusinessUrl = "",
                        AuditUrl = "",
                        ModelKey = "",
                        Before = "",
                        After = "",
                        SkipEvent = "",
                        Size = "",
                        d.StableVersion,
                        d.F_DeleteMark,
                        d.F_CreateTime,
                        d.F_CreateUser,
                        d.F_LastUpdateTime,
                        d.F_LastUpdateUser,
                        CreateUserName = cu.F_RealName == null ? cu.F_NickName : cu.F_RealName
                    }).LeftOuterJoin(db.Set<SysUserEntity>(), d => d.F_LastUpdateUser, uu => uu.F_Id, (d, uu) => new
                    {
                        d.Id,
                        d.CateId,
                        d.Name,
                        d.Remark,
                        d.Version,
                        d.StableVersion,
                        d.BusinessUrl,
                        d.AuditUrl,
                        d.ModelKey,
                        d.Before,
                        d.After,
                        d.SkipEvent,
                        d.Size,
                        d.F_DeleteMark,
                        d.F_CreateTime,
                        d.F_CreateUser,
                        d.F_LastUpdateTime,
                        d.F_LastUpdateUser,
                        d.CreateUserName,
                        LastUpdateUserName = uu.F_RealName == null ? uu.F_NickName : uu.F_RealName
                    }).LeftOuterJoin(db.Set<WFCategoryEntity>(), d => d.CateId, c => c.Id, (d, c) => new
                    {
                        d.Id,
                        d.CateId,
                        CateName = c.Name,
                        d.Name,
                        d.Remark,
                        d.Version,
                        d.StableVersion,
                        d.BusinessUrl,
                        d.AuditUrl,
                        d.ModelKey,
                        d.Before,
                        d.After,
                        d.SkipEvent,
                        d.Size,
                        d.F_DeleteMark,
                        d.F_CreateTime,
                        d.F_CreateUser,
                        d.F_LastUpdateTime,
                        d.F_LastUpdateUser,
                        d.CreateUserName,
                        d.LastUpdateUserName
                    });

                    if (queryInfo.version.HasValue)
                    {
                        search = search.Join(db.Set<WFVersionEntity>().Where(v => v.Version == queryInfo.version.Value), d => d.Id, v => v.WFID, (d, v) => new
                        {
                            d.Id,
                            d.CateId,
                            d.CateName,
                            d.Name,
                            d.Remark,
                            v.Version,//当前版本
                            d.StableVersion,
                            v.BusinessUrl,
                            v.AuditUrl,
                            v.ModelKey,
                            v.Before,
                            v.After,
                            v.SkipEvent,
                            v.Size,
                            d.F_DeleteMark,
                            d.F_CreateTime,
                            d.F_CreateUser,
                            d.F_LastUpdateTime,
                            d.F_LastUpdateUser,
                            d.CreateUserName,
                            d.LastUpdateUserName
                        });
                    }
                    else if (queryInfo.stable)
                    {
                        search = search.Join(db.Set<WFVersionEntity>(), d => new { id = d.Id, version = d.StableVersion }, v => new { id = v.WFID, version = v.Version }, (d, v) => new
                        {
                            d.Id,
                            d.CateId,
                            d.CateName,
                            d.Name,
                            d.Remark,
                            d.Version,
                            d.StableVersion,
                            v.BusinessUrl,
                            v.AuditUrl,
                            v.ModelKey,
                            v.Before,
                            v.After,
                            v.SkipEvent,
                            v.Size,
                            d.F_DeleteMark,
                            d.F_CreateTime,
                            d.F_CreateUser,
                            d.F_LastUpdateTime,
                            d.F_LastUpdateUser,
                            d.CreateUserName,
                            d.LastUpdateUserName
                        });
                    }
                    else
                    {
                        search = search.Join(db.Set<WFVersionEntity>(), d => new { id = d.Id, version = d.Version }, v => new { id = v.WFID, version = v.Version }, (d, v) => new
                        {
                            d.Id,
                            d.CateId,
                            d.CateName,
                            d.Name,
                            d.Remark,
                            d.Version,
                            d.StableVersion,
                            v.BusinessUrl,
                            v.AuditUrl,
                            v.ModelKey,
                            v.Before,
                            v.After,
                            v.SkipEvent,
                            v.Size,
                            d.F_DeleteMark,
                            d.F_CreateTime,
                            d.F_CreateUser,
                            d.F_LastUpdateTime,
                            d.F_LastUpdateUser,
                            d.CreateUserName,
                            d.LastUpdateUserName
                        });
                    }

                    if (!string.IsNullOrEmpty(queryInfo.keyword))
                    {
                        search = search.Where(d => d.Name.Contains(queryInfo.keyword));
                    }

                    List<WorkFlowDef> list = search.ToList().Select(d => new WorkFlowDef()
                    {
                        id = d.Id,
                        name = d.Name,
                        version = d.Version,
                        stableVersion = d.StableVersion,
                        cateId = d.CateId,
                        cateName = d.CateName,
                        businessUrl = d.BusinessUrl,
                        auditUrl = d.AuditUrl,
                        modelKey = d.ModelKey,
                        deleteMark = d.F_DeleteMark,
                        before = d.Before,
                        after = d.After,
                        skipEvent = d.SkipEvent,
                        size = d.Size,
                        remark = d.Remark,
                        createTime = d.F_CreateTime.ToString("yyyy-MM-dd HH:mm:ss"),
                        createUser = d.CreateUserName,
                        lastUpdateTime = d.F_LastUpdateTime.HasValue ? d.F_LastUpdateTime.Value.ToString("yyyy-MM-dd HH:mm:ss") : null,
                        lastUpdateUser = d.LastUpdateUserName
                    }).ToList();

                    if (!string.IsNullOrEmpty(queryInfo.id) && list.Count > 0)
                    {
                        WorkFlowDef def = list[0];
                        def.nodes = db.Set<WFNodeEntity>().Where(n => n.WFID == def.id && n.Version == def.version)
                            .ToList()
                            .Select(n => new WorkFlowNode
                            {
                                id = n.Id,
                                code = n.Code,
                                type = n.NodeType,
                                name = n.Name,
                                remark = n.Remark,
                                rectangle = n.Rectangle,
                                comment = n.Comment,
                                businessUrl = n.BusinessUrl,
                                auditUrl = n.AuditUrl,
                                modelKey = n.ModelKey,
                                before = n.Before,
                                after = n.After
                            }).ToList();

                        List<string> nodeIds = def.nodes.Select(n => n.id).ToList();
                        def.nodeLinks = db.Set<WFNodeLinkEntity>().Where(nl => nodeIds.Contains(nl.StartNode) || nodeIds.Contains(nl.EndNode))
                            .ToList()
                            .Select(nl => new WorkFlowNodeLink
                            {
                                id = nl.Id,
                                text = nl.Text,
                                textPosition = nl.TextPosition,
                                linkType = nl.LinkType,
                                startNode = nl.StartNode,
                                endNode = nl.EndNode,
                                condition = nl.Condition,
                                startPosition = nl.StartPosition,
                                endPosition = nl.EndPosition,
                                style = nl.Style
                            }).ToList();

                        List<WorkFlowNodeUser> nodeUsers = db.Set<WFNodeUserEntity>().Where(nu => nodeIds.Contains(nu.NodeId)).ToList()
                            .Select(nu => new WorkFlowNodeUser
                            {
                                id = nu.Id.ToString(),
                                nodeId = nu.NodeId,
                                userId = nu.UserId,
                                userName = nu.UserName,
                                userType = nu.UserType,
                                isCandidate = nu.IsCandidate
                            }).ToList();

                        List<WorkFlowNodeProperty> nodeProps = db.Set<WFNodePropertyEntity>().Where(np => nodeIds.Contains(np.NodeId)).ToList()
                            .Select(np => new WorkFlowNodeProperty
                            {
                                nodeId = np.NodeId,
                                name = np.Name,
                                key = np.Key,
                                value = np.Value,
                                type = np.PropType
                            }).ToList();

                        def.nodes.ForEach(n=> {
                            n.users = nodeUsers.Where(nu => nu.nodeId == n.id).ToList();
                            n.properties = nodeProps.Where(np => np.nodeId == n.id).ToList();
                        });
                    }

                    return list;
                }
            }
        }

        /// <summary>
        /// 发起流程
        /// </summary>
        /// <param name="id">流程定义</param>
        /// <param name="userId">用户ID</param>
        /// <returns></returns>
        public EditItemResult StartFlow(string id, string userId)
        {
            if (string.IsNullOrEmpty(userId) || string.IsNullOrEmpty(id))
            {
                return EditItemResult.Fail("流程不可用");
            }

            using (var scope = OperatorProvider.BeginLifetimeScope(null))
            {
                using (var db = scope.Resolve<DbContext>())
                {
                    WFDefEntity wf = db.Set<WFDefEntity>().Where(d => d.Id == id && !d.F_DeleteMark).FirstOrDefault();
                    if (wf == null || wf.StableVersion == 0)
                    {
                        return EditItemResult.Fail("流程不可用");
                    }

                    WFVersionEntity wfVersion = db.Set<WFVersionEntity>().Where(v => v.WFID == id
                    && v.Version == wf.StableVersion).FirstOrDefault();

                    if (wfVersion == null)
                    {
                        return EditItemResult.Fail("流程不可用");
                    }

                    WorkFlowNode startNode = db.Set<WFNodeEntity>().Where(n => n.WFID == id
                    && n.Version == wf.StableVersion && n.NodeType == (int)WFNodeType.Start)
                        .Select(n=> new WorkFlowNode {
                        id = n.Id,
                        name = n.Name,
                        type = n.NodeType,
                        code = n.Code,
                        remark = n.Remark,
                        comment = n.Comment,
                        businessUrl = n.BusinessUrl,
                        auditUrl = n.AuditUrl,
                        modelKey = n.ModelKey,
                        before = n.Before,
                        after = n.After
                    }).FirstOrDefault();

                    if(startNode == null)
                    {
                        return EditItemResult.Fail("流程配置不包含开始节点");
                    }

                    if(string.IsNullOrEmpty(startNode.businessUrl))
                    {
                        startNode.businessUrl = wfVersion.BusinessUrl;
                    }

                    if (string.IsNullOrEmpty(startNode.modelKey))
                    {
                        startNode.modelKey = wfVersion.ModelKey;
                    }

                    startNode.size = wfVersion.Size;

                    var user = db.Set<SysUserEntity>().Where(u => u.F_Id == userId).Select(u => new
                    {
                        UserId = u.F_Id,
                        UserName = u.F_RealName == null ? u.F_NickName : u.F_RealName,
                        OrgId = u.F_OrganizeId,
                        Role = u.F_RoleId,
                        DepartId = u.F_DepartmentId,
                        PostId = u.F_DutyId
                    }).FirstOrDefault();

                    if (user == null)
                    {
                        return EditItemResult.Fail("流程发起人不存在");
                    }

                    DbContextTransaction trans = db.Database.BeginTransaction();
                    try
                    {
                        WFInstanceEntity flow = new WFInstanceEntity();
                        flow.Id = Guid.NewGuid().ToString();
                        flow.WFID = wf.Id;
                        flow.Version = wf.StableVersion;
                        flow.Name = user.UserName + wf.Name;
                        flow.F_CreateTime = DateTime.Now;
                        flow.F_CreateUser = user.UserId;
                        flow.F_DeleteMark = false;
                        flow.Remark = string.IsNullOrEmpty(startNode.name) ? "新建" : startNode.name;
                        flow.NodeId = startNode.id;

                        IWFInstanceRepository repos = OperatorProvider.Resolve<IWFInstanceRepository>();
                        int count = repos.Insert(db, flow);

                        WorkFlowBeforeEventContext context = new WorkFlowBeforeEventContext();
                        context.instance = flow;
                        context.after = wfVersion.After;
                        context.dao = db;
                        context.cancel = false;
                        context.userId = userId;
                        context.from = null;
                        context.to = startNode;

                        //流程开始事件
                        if (!string.IsNullOrEmpty(wfVersion.Before))
                        {
                            string error = null;
                            //事件中可以改变节点属性
                            MethodInfo before = WorkFlowHelper.GetEventMethod(wfVersion.Before, BeforeEventArgs, out error);
                            if (error != null)
                            {
                                throw new ArgumentException(error);
                            }

                            before.Invoke(null, new object[] { context });
                            if (context.cancel)
                            {
                                throw new OperationCanceledException(context.message);
                            }

                            if (context.error != null)
                            {
                                throw context.error;
                            }
                        }

                        if (string.IsNullOrEmpty(startNode.businessUrl)
                            && string.IsNullOrEmpty(startNode.auditUrl))
                        {
                            StringBuilder path = new StringBuilder();
                            Next(context, path);
                            if (context.instance == null)
                            {
                                trans.Rollback();
                                return EditItemResult.Fail("发起流程失败");
                            }

                            trans.Commit();
                            if (string.IsNullOrEmpty(context.instance.NodeId))
                            {
                                return EditItemResult.Ok("流程自动完成");
                            }
                            else
                            {
                                return EditItemResult.Ok("新建流程成功");
                            }
                        }
                        else
                        {
                            trans.Commit();
                            //页面处理（待办）
                            SendFlowMsg(flow, startNode.id, userId);
                            //有操作页面
                            return EditItemResult.Ok(null, $"{startNode.size}:{flow.Id},{startNode.id}");
                        }
                    }
                    catch (Exception ex)
                    {
                        try
                        {
                            trans.Rollback();
                        }
                        catch
                        {
                            //rollback error
                        }
                        return EditItemResult.Fail("发起流程失败：" + Common.ShowException(ex));
                    }
                }
            }
        }

        #region 流程实例
        /// <summary>
        /// 流程实例查询
        /// </summary>
        /// <param name="queryInfo">流程实例查询</param>
        /// <returns>查询结果</returns>
        public PagedResult QueryFlow(WFInstanceQueryInfo queryInfo)
        {
            if (queryInfo == null || string.IsNullOrEmpty(queryInfo.userId))
            {
                return new PagedResult();
            }

            using (var scope = OperatorProvider.BeginLifetimeScope(null))
            {
                using (var db = scope.Resolve<DbContext>())
                {
                    IQueryable<WFInstanceEntity> query = db.Set<WFInstanceEntity>()
                        .Where(i => !i.F_DeleteMark);

                    if (!string.IsNullOrEmpty(queryInfo.id))
                    {
                        query = query.Where(i => i.Id == queryInfo.id);
                    }

                    if (!string.IsNullOrEmpty(queryInfo.wfid))
                    {
                        query = query.Where(i => i.WFID == queryInfo.wfid);
                    }

                    if (queryInfo.version > 0)
                    {
                        query = query.Where(i => i.Version == queryInfo.version);
                    }

                    if (!string.IsNullOrEmpty(queryInfo.remark))
                    {
                        query = query.Where(i => i.Remark != null && i.Remark.Contains(queryInfo.remark));
                    }

                    IQueryable<WFMessageEntity> msgs = db.Set<WFMessageEntity>();
                    if (!queryInfo.isSys)
                    {
                        query = query.Where(i => i.F_CreateUser == queryInfo.userId
                        || msgs.Any(m => m.InstanceId == i.Id && m.OperateMan == queryInfo.userId));
                    }

                    var search = query.Join(db.Set<WFDefEntity>().Where(d=>d.F_OrganizeId == queryInfo.orgId), i => i.WFID, d => d.Id, (i, d) => new
                    {
                        i.Id,
                        i.NodeId,
                        i.Name,
                        i.Remark,
                        i.F_CreateTime,
                        i.F_CreateUser,
                        i.F_LastUpdateTime,
                        i.F_LastUpdateUser,
                        i.WFID,
                        i.Version,
                        d.CateId
                    }).Join(db.Set<WFCategoryEntity>(), i => i.CateId, c => c.Id, (i, c) => new
                    {
                        i.Id,
                        i.NodeId,
                        i.Name,
                        i.Remark,
                        i.F_CreateTime,
                        i.F_CreateUser,
                        i.F_LastUpdateTime,
                        i.F_LastUpdateUser,
                        i.WFID,
                        i.Version,
                        i.CateId,
                        CateName = c.Name,
                        c.ParentId
                    }).LeftOuterJoin(db.Set<WFCategoryEntity>(), i => i.ParentId, pc => pc.Id, (i, pc) => new
                    {
                        i.Id,
                        i.NodeId,
                        i.Name,
                        i.Remark,
                        i.F_CreateTime,
                        i.F_CreateUser,
                        i.F_LastUpdateTime,
                        i.F_LastUpdateUser,
                        i.WFID,
                        i.Version,
                        i.CateId,
                        CateName = pc.Name == null ? i.CateName : (pc.Name + "-" + i.CateName)
                    }).LeftOuterJoin(db.Set<SysUserEntity>(), i => i.F_CreateUser, cu => cu.F_Id, (i, cu) => new
                    {
                        i.Id,
                        i.NodeId,
                        i.Name,
                        i.Remark,
                        i.F_CreateTime,
                        i.F_CreateUser,
                        i.F_LastUpdateTime,
                        i.F_LastUpdateUser,
                        i.WFID,
                        i.Version,
                        i.CateId,
                        i.CateName,
                        F_CreateUserName = cu.F_RealName == null ? cu.F_NickName : cu.F_RealName
                    }).LeftOuterJoin(db.Set<SysUserEntity>(), i => i.F_LastUpdateUser, uu => uu.F_Id, (i, uu) => new
                    {
                        i.Id,
                        i.NodeId,
                        i.Name,
                        i.Remark,
                        i.F_CreateTime,
                        i.F_CreateUser,
                        i.F_LastUpdateTime,
                        i.F_LastUpdateUser,
                        i.WFID,
                        i.Version,
                        i.CateId,
                        i.CateName,
                        i.F_CreateUserName,
                        F_LastUpdateUserName = uu.F_RealName == null ? uu.F_NickName : uu.F_RealName
                    });

                    int total = search.Count();
                    List<WorkFlowInstance> rows = search.OrderBy(i => i.F_LastUpdateTime == null ? i.F_CreateTime : i.F_LastUpdateTime)
                        .Skip((queryInfo.page - 1) * queryInfo.rows).Take(queryInfo.rows)
                        .ToList().Select(i => new WorkFlowInstance()
                        {
                            id = i.Id,
                            name = i.Name,
                            cateId = i.CateId,
                            cateName = i.CateName,
                            wfid = i.WFID,
                            version = i.Version,
                            createTime = i.F_CreateTime.ToString("yyyy-MM-dd HH:mm:ss"),
                            createUser = i.F_CreateUser,
                            createUserName = i.F_CreateUserName,
                            isOwner = i.F_CreateUser == queryInfo.userId,
                            lastUpdateTime = i.F_LastUpdateTime?.ToString("yyyy-MM-dd HH:mm:ss"),
                            lastUpdateUser = i.F_LastUpdateUser,
                            lastUpdateUserName = i.F_LastUpdateUserName,
                            remark = i.Remark,
                            nodeId = i.NodeId
                        }).ToList();

                    if (!string.IsNullOrEmpty(queryInfo.id) && rows.Count == 1)
                    {
                        WorkFlowInstance instance = rows[0];
                        instance.nodes = FlowNodeList(db, instance.id);
                    }

                    return new PagedResult()
                    {
                        records = total,
                        total = (int)Math.Ceiling((float)total / queryInfo.rows),
                        page = queryInfo.page,
                        rows = rows
                    };
                }
            }
        }

        /// <summary>
        /// 流程实例流转
        /// </summary>
        /// <param name="id">流程实例ID</param>
        /// <returns>查询结果</returns>
        public List<WorkFlowInstanceNode> FlowNodeList(string id)
        {
            if (string.IsNullOrEmpty(id))
            {
                return new List<WorkFlowInstanceNode>();
            }

            using (var scope = OperatorProvider.BeginLifetimeScope(null))
            {
                using (var db = scope.Resolve<DbContext>())
                {
                    return FlowNodeList(db,id);
                }
            }
        }

        /// <summary>
        /// 处理流程
        /// </summary>
        /// <param name="model">流程处理对象</param>
        /// <returns></returns>
        public ProcessFlowResult Process(ProcessFlowModel model)
        {
            if (model == null
                || string.IsNullOrEmpty(model.flowId)
                || string.IsNullOrEmpty(model.nodeId)
                || string.IsNullOrEmpty(model.userId))
            {
                return new ProcessFlowResult { state = false, msg = "流程不存在" };
            }

            ProcessFlowResult result = new ProcessFlowResult();
            result.instanceId = model.flowId;
            result.nodeId = string.IsNullOrEmpty(model.flowNodeId) ? model.nodeId : $"{model.flowNodeId}:{model.nodeId}";

            using (var scope = OperatorProvider.BeginLifetimeScope(null))
            {
                using (var db = scope.Resolve<DbContext>())
                {
                    WFMessageEntity msg = db.Set<WFMessageEntity>().Where(m => m.InstanceId == model.flowId 
                    && m.NodeId == result.nodeId && m.OperateMan == model.userId).FirstOrDefault();
                    if (msg == null)
                    {
                        result.msg = "待处理的流程不存在";
                        return result;
                    }

                    if (msg.State > 0)
                    {
                        result.state = true;
                        return result;
                    }

                    WFInstanceEntity flow = db.Set<WFInstanceEntity>().Where(i => i.Id == msg.InstanceId).FirstOrDefault();
                    if (flow == null)
                    {
                        result.msg = "待处理的流程不存在";
                        return result;
                    }

                    if(string.IsNullOrEmpty(flow.NodeId))
                    {
                        result.state = true;
                        return result;
                    }

                    string[] nodeIds = flow.NodeId.Split(',');
                    if (!nodeIds.Contains(result.nodeId))
                    {
                        return new ProcessFlowResult() { state = true };
                    }

                    WorkFlowNode node = db.Set<WFNodeEntity>().Where(n => n.Id == model.nodeId)
                        .Join(db.Set<WFVersionEntity>(), n => new WorkFlowVersionKey { Id = n.WFID, Version = n.Version },
                        v => new WorkFlowVersionKey { Id = v.WFID, Version = v.Version },
                        (n, v) => new WorkFlowNode
                        {
                            id = n.Id,
                            name = n.Name,
                            type = n.NodeType,
                            code = n.Code,
                            remark = n.Remark,
                            comment = n.Comment,
                            businessUrl = n.BusinessUrl == null || n.BusinessUrl == "" ? v.BusinessUrl : n.BusinessUrl,
                            auditUrl = (n.NodeType == (int)WFNodeType.Start || n.NodeType == (int)WFNodeType.End) ? null : (n.AuditUrl == null || n.AuditUrl == "" ? v.AuditUrl : n.AuditUrl),
                            modelKey = n.ModelKey == null || n.ModelKey == "" ? v.ModelKey : n.ModelKey,
                            before = n.Before,
                            after = n.After,
                        }).FirstOrDefault();
                    if (node == null)
                    {
                        result.msg = "待处理的流程不存在";
                        return result;
                    }

                    long? flowNodeId = null;
                    WFInstanceNodeEntity flowNode = null;
                    if (!string.IsNullOrEmpty(model.flowNodeId))
                    {
                        flowNodeId = long.Parse(model.flowNodeId);
                        flowNode = db.Set<WFInstanceNodeEntity>().Where(fn => fn.Id == flowNodeId.Value).FirstOrDefault();
                    }

                    DbContextTransaction trans = db.Database.BeginTransaction();

                    try
                    {
                        GetDefNodeProps(db, node);

                        if (model.props != null && model.props.Count > 0)
                        {
                            model.props.ForEach(p =>
                            {
                                if (!string.IsNullOrEmpty(p.key))
                                {
                                    WorkFlowNodeProperty prop = node.properties.FirstOrDefault(np => np.type == p.type && np.key == p.key);
                                    if (prop == null)
                                    {
                                        node.properties.Add(p);
                                    }
                                    else
                                    {
                                        prop.value = p.value;
                                    }
                                }
                            });
                        }

                        WFInstanceNodeEntity iNode = new WFInstanceNodeEntity();
                        iNode.FromNode = flowNodeId;
                        iNode.FromNodeName = flowNode?.NodeName;
                        iNode.NodeType = node.type;
                        iNode.NodeId = node.id;
                        iNode.NodeName = string.IsNullOrEmpty(node.name) ? (node.type == (int)WFNodeType.Start ? "开始流程" : node.type == (int)WFNodeType.End ? "结束流程" : "处理完成") : node.name;
                        iNode.InstanceId = model.flowId;
                        iNode.F_CreateTime = DateTime.Now;
                        iNode.F_CreateUser = model.userId;
                        iNode.Remark = AddFlowNodeResult(node, true);
                        
                        WorkFlowInstanceNode from = null;
                        if (flowNode != null)
                        {
                            from = new WorkFlowInstanceNode();
                            from.instanceId = flowNode.InstanceId;
                            from.createTime = flowNode.F_CreateTime.ToString("yyyy-MM-dd HH:mm:ss");
                            from.createUser = flowNode.F_CreateUser;
                            from.fromNode = flowNode.FromNode?.ToString();
                            from.fromNodeName = flowNode.FromNodeName;
                            from.nodeId = flowNode.NodeId;
                            from.nodeType = flowNode.NodeType;
                            from.nodeName = node.name;
                            from.modelKey = node.modelKey;
                            from.after = node.after;
                            from.ID = flowNode.Id;
                            from.id = flowNode.Id.ToString();
                        }

                        WorkFlowBeforeEventContext context = new WorkFlowBeforeEventContext();
                        context.instance = flow;
                        context.dao = db;
                        context.cancel = false;
                        context.userId = model.userId;
                        context.from = from;
                        context.to = node;

                        if (!string.IsNullOrEmpty(context.to.before))
                        {
                            string error = null;
                            //事件中可以改变节点属性
                            MethodInfo before = WorkFlowHelper.GetEventMethod(context.to.before, BeforeEventArgs, out error);
                            if (error != null)
                            {
                                trans.Rollback();
                                result.msg = error;
                                return result;
                            }

                            before.Invoke(null, new object[] { context });
                            if (context.cancel)
                            {
                                trans.Rollback();
                                result.msg = string.IsNullOrEmpty(context.message) ? "开始事件取消执行" : context.message;
                                return result;
                            }

                            if (context.error != null)
                            {
                                trans.Rollback();
                                result.msg = string.IsNullOrEmpty(context.message) ? Common.ShowException(context.error) : context.message;
                                return result;
                            }
                        }

                        IWFInstanceNodeRepository nodeRepos = OperatorProvider.Resolve<IWFInstanceNodeRepository>();
                        nodeRepos.Insert(db, iNode);

                        from = new WorkFlowInstanceNode();
                        from.instanceId = iNode.InstanceId;
                        from.createTime = iNode.F_CreateTime.ToString("yyyy-MM-dd HH:mm:ss");
                        from.createUser = iNode.F_CreateUser;
                        from.fromNode = model.flowNodeId;
                        from.fromNodeName = iNode.NodeName;
                        from.nodeId = iNode.NodeId;
                        from.nodeType = iNode.NodeType;
                        from.nodeName = node.name;
                        from.modelKey = node.modelKey;
                        from.after = node.after;
                        from.ID = iNode.Id;
                        from.id = iNode.Id.ToString();
                        context.from = from;
                        context.to = null;
                        //重新提取业务数据
                        GetFlowNodeProps(context.dao, from,1);
                        List<WFInstanceNodePropertyEntity> props = FilterFlowProperty(iNode.Id,node.properties,from.properties);
                        if (props.Count > 0)
                        {
                            IWFInstanceNodePropertyRepository pRepos = OperatorProvider.Resolve<IWFInstanceNodePropertyRepository>();
                            pRepos.Insert(db, props);
                        }
                        from.properties.AddRange(node.properties.Where(p => p.type == 0));
                        //提取业务数据
                        ExecuteAfterEvent(context, from.after, from.nodeName);

                        StringBuilder path = new StringBuilder();
                        Next(context, path);
                        if (context.cancel)
                        {
                            trans.Rollback();
                            result.msg = string.IsNullOrEmpty(context.message) ? "操作被取消" : context.message;
                            return result;
                        }

                        if (context.error != null)
                        {
                            trans.Rollback();
                            result.msg = string.IsNullOrEmpty(context.message) ? Common.ShowException(context.error) : context.message;
                            return result;
                        }

                        trans.Commit();
                        result.state = true;
                        return result;
                    }
                    catch(Exception ex)
                    {
                        try
                        {
                            trans.Rollback();
                        }
                        catch
                        {
                            //回滚失败
                        }
                        result.msg = Common.ShowException(ex);
                        return result;
                    }
                }
            }
        }

        /// <summary>
        /// 终止流程
        /// </summary>
        /// <param name="flowId">流程id</param>
        /// <param name="userId">操作用户</param>
        public EditItemResult StopFlow(string flowId, string userId)
        {
            if (string.IsNullOrEmpty(flowId) || string.IsNullOrEmpty(userId))
            {
                return EditItemResult.Fail("参数无效");
            }

            using (var scope = OperatorProvider.BeginLifetimeScope(null))
            {
                using (var db = scope.Resolve<DbContext>())
                {
                    
                    WFInstanceEntity flow = db.Set<WFInstanceEntity>().Where(i => i.Id == flowId).FirstOrDefault();
                    if (flow == null)
                    {
                        return EditItemResult.Fail("流程不可用");
                    }

                    if (flow.F_CreateUser != userId)
                    {
                        return EditItemResult.Fail("非发起人不能停止流程");
                    }

                    if (string.IsNullOrEmpty(flow.NodeId))
                    {
                        return EditItemResult.Ok();
                    }

                    WFVersionEntity flowVer = db.Set<WFVersionEntity>().Where(v => v.WFID == flow.WFID
                    && v.Version == flow.Version).FirstOrDefault();
                    if (flowVer == null)
                    {
                        return EditItemResult.Fail("流程不可用");
                    }

                    WorkFlowNode endNode = db.Set<WFNodeEntity>().Where(n => n.WFID == flow.WFID && n.Version == flow.Version
                    && n.NodeType == (int)WFNodeType.End).Select(n => new WorkFlowNode
                    {
                        id = n.Id,
                        name = n.Name,
                        type = n.NodeType,
                        code = n.Code,
                        remark = n.Remark,
                        comment = n.Comment,
                        businessUrl = n.BusinessUrl,
                        auditUrl = n.AuditUrl,
                        modelKey = n.ModelKey,
                        before = n.Before,
                        after = n.After,
                    }).FirstOrDefault();

                    if (endNode == null)
                    {
                        return EditItemResult.Fail("流程没有结束节点");
                    }

                    DbContextTransaction trans = db.Database.BeginTransaction();

                    try
                    {
                        WorkFlowBeforeEventContext context = new WorkFlowBeforeEventContext();
                        context.instance = flow;
                        context.dao = db;
                        context.cancel = false;
                        context.userId = userId;
                        context.from = null;
                        context.to = endNode;

                        string flowNodeId = flow.NodeId;

                        GetDefNodeProps(context.dao, context.to);
                        AddFlowNodeResult(context.to, true);

                        if (endNode != null && !string.IsNullOrEmpty(context.to.before))
                        {
                            string error = null;
                            //事件中可以改变节点属性
                            MethodInfo before = WorkFlowHelper.GetEventMethod(context.to.before, BeforeEventArgs, out error);
                            if (error != null)
                            {
                                return EditItemResult.Fail(error);
                            }

                            before.Invoke(null, new object[] { context });
                            if (context.cancel)
                            {
                                return EditItemResult.Fail(string.IsNullOrEmpty(context.message) ? "结束节点开始事件取消执行" : context.message);
                            }

                            if (context.error != null)
                            {
                                return EditItemResult.Fail(string.IsNullOrEmpty(context.message) ? Common.ShowException(context.error) : context.message);
                            }
                        }

                        DeleteFlowMsgs(db, flow.Id, flowNodeId.Split(','), "停止流程");

                        WFInstanceNodeEntity iNode = new WFInstanceNodeEntity();
                        iNode.FromNode = null;
                        iNode.FromNodeName = null;
                        iNode.NodeType = context.to.type;
                        iNode.NodeId = context.to.id;
                        iNode.NodeName = string.IsNullOrEmpty(context.to.name) ? "结束流程" : context.to.name;
                        iNode.InstanceId = context.instance.Id;
                        iNode.F_CreateTime = DateTime.Now;
                        iNode.F_CreateUser = context.userId;
                        iNode.Remark = "停止流程";

                        IWFInstanceNodeRepository nodeRepos = OperatorProvider.Resolve<IWFInstanceNodeRepository>();
                        nodeRepos.Insert(context.dao, iNode);

                        List<WFInstanceNodePropertyEntity> props = FilterFlowProperty(iNode.Id,context.to.properties,null);

                        if (props.Count > 0)
                        {
                            IWFInstanceNodePropertyRepository pRepos = OperatorProvider.Resolve<IWFInstanceNodePropertyRepository>();
                            pRepos.Insert(context.dao, props);
                        }

                        ExecuteAfterEvent(context, endNode.after, iNode.NodeName);
                        ExecuteAfterEvent(context, flowVer.After,null);

                        //不同于正常结束流程，先触发结束事件
                        flow.F_LastUpdateTime = DateTime.Now;
                        flow.F_LastUpdateUser = userId;
                        flow.Remark = "停止流程";
                        flow.NodeId = null;
                        IWFInstanceRepository flowRepos = OperatorProvider.Resolve<IWFInstanceRepository>();
                        flowRepos.Update(db, UpdateFlowColumns, flow);
                        trans.Commit();

                        return EditItemResult.Ok();
                    }
                    catch(Exception ex)
                    {
                        try
                        {
                            trans.Rollback();
                        }
                        catch
                        {
                            //回滚失败
                        }
                        return EditItemResult.Fail(Common.ShowException(ex));
                    }
                }
            }
        }

        /// <summary>
        /// 获取流程实例
        /// </summary>
        /// <param name="flowId">流程ID</param>
        /// <param name="nodeId">当前处理节点</param>
        /// <param name="userId">当前操作用户</param>
        /// <returns></returns>
        public WorkFlowInstance GetWorkFlow(string flowId, string nodeId, string userId)
        {
            if (string.IsNullOrEmpty(flowId)
                || string.IsNullOrEmpty(nodeId)
                || string.IsNullOrEmpty(userId))
            {
                return new WorkFlowInstance() { error = "处理参数无效" };
            }

            using (var scope = OperatorProvider.BeginLifetimeScope(null))
            {
                using (var db = scope.Resolve<DbContext>())
                {
                    WFMessageEntity msg = db.Set<WFMessageEntity>().Where(m => m.InstanceId == flowId
                    && m.NodeId == nodeId && m.OperateMan == userId).FirstOrDefault();
                    if (msg == null)
                    {
                        return new WorkFlowInstance() { error = "流程待处理过程不存在" };
                    }

                    if (msg.State == 1)
                    {
                        return new WorkFlowInstance() { error = "流程过程已处理" };
                    }
                    else if (msg.State == 2)
                    {
                        return new WorkFlowInstance() { error = "流程过程已无效" };
                    }

                    var entity = db.Set<WFInstanceEntity>().Where(i => i.Id == flowId)
                        .Join(db.Set<WFDefEntity>(), i => i.WFID, d => d.Id, (i, d) => new
                        {
                            i.Id,
                            i.NodeId,
                            i.Name,
                            i.Remark,
                            i.F_CreateTime,
                            i.F_CreateUser,
                            i.F_LastUpdateTime,
                            i.F_LastUpdateUser,
                            i.WFID,
                            i.Version,
                            d.CateId
                        }).Join(db.Set<WFCategoryEntity>(), i => i.CateId, c => c.Id, (i, c) => new
                        {
                            i.Id,
                            i.NodeId,
                            i.Name,
                            i.Remark,
                            i.F_CreateTime,
                            i.F_CreateUser,
                            i.F_LastUpdateTime,
                            i.F_LastUpdateUser,
                            i.WFID,
                            i.Version,
                            i.CateId,
                            CateName = c.Name,
                            c.ParentId
                        }).LeftOuterJoin(db.Set<WFCategoryEntity>(), i => i.ParentId, pc => pc.Id, (i, pc) => new
                        {
                            i.Id,
                            i.NodeId,
                            i.Name,
                            i.Remark,
                            i.F_CreateTime,
                            i.F_CreateUser,
                            i.F_LastUpdateTime,
                            i.F_LastUpdateUser,
                            i.WFID,
                            i.Version,
                            i.CateId,
                            CateName = pc.Name == null ? i.CateName : (pc.Name + "-" + i.CateName)
                        }).LeftOuterJoin(db.Set<SysUserEntity>(), i => i.F_CreateUser, cu => cu.F_Id, (i, cu) => new
                        {
                            i.Id,
                            i.NodeId,
                            i.Name,
                            i.Remark,
                            i.F_CreateTime,
                            i.F_CreateUser,
                            i.F_LastUpdateTime,
                            i.F_LastUpdateUser,
                            i.WFID,
                            i.Version,
                            i.CateId,
                            i.CateName,
                            F_CreateUserName = cu.F_RealName == null ? cu.F_NickName : cu.F_RealName
                        }).LeftOuterJoin(db.Set<SysUserEntity>(), i => i.F_LastUpdateUser, uu => uu.F_Id, (i, uu) => new
                        {
                            i.Id,
                            i.NodeId,
                            i.Name,
                            i.Remark,
                            i.F_CreateTime,
                            i.F_CreateUser,
                            i.F_LastUpdateTime,
                            i.F_LastUpdateUser,
                            i.WFID,
                            i.Version,
                            i.CateId,
                            i.CateName,
                            i.F_CreateUserName,
                            F_LastUpdateUserName = uu.F_RealName == null ? uu.F_NickName : uu.F_RealName
                        }).FirstOrDefault();

                    if (entity == null)
                    {
                        return new WorkFlowInstance() { error = "流程不存在" };
                    }

                    WorkFlowInstance flow = new WorkFlowInstance
                    {
                        id = entity.Id,
                        name = entity.Name,
                        cateId = entity.CateId,
                        cateName = entity.CateName,
                        wfid = entity.WFID,
                        version = entity.Version,
                        createTime = entity.F_CreateTime.ToString("yyyy-MM-dd HH:mm:ss"),
                        createUser = entity.F_CreateUser,
                        createUserName = entity.F_CreateUserName,
                        isOwner = entity.F_CreateUser == userId,
                        lastUpdateTime = entity.F_LastUpdateTime?.ToString("yyyy-MM-dd HH:mm:ss"),
                        lastUpdateUser = entity.F_LastUpdateUser,
                        lastUpdateUserName = entity.F_LastUpdateUserName,
                        remark = entity.Remark,
                        nodeId = entity.NodeId
                    };

                    flow.nodes = FlowNodeList(db, flowId);

                    long? flowNodeId = null;
                    string defNode = nodeId;
                    int idx = defNode.IndexOf(":");
                    WorkFlowInstanceNode flowNode = null;
                    if (idx > -1)
                    {
                        flow.flowNodeId = defNode.Substring(0, idx);
                        flowNodeId = long.Parse(flow.flowNodeId);
                        flowNode = flow.nodes.Where(n => n.ID == flowNodeId).FirstOrDefault();
                        GetFlowNodeProps(db, flowNode);
                        defNode = defNode.Substring(idx + 1);
                    }

                    flow.node = db.Set<WFNodeEntity>().Where(n => n.Id == defNode)
                        .Join(db.Set<WFVersionEntity>(), n => new WorkFlowVersionKey { Id = n.WFID, Version = n.Version },
                        v => new WorkFlowVersionKey { Id = v.WFID, Version = v.Version }, (n, v) => new WorkFlowNode
                        {
                            id = n.Id,
                            name = n.Name,
                            type = n.NodeType,
                            code = n.Code,
                            remark = n.Remark,
                            comment = n.Comment,
                            before = n.Before,
                            after = n.After,
                            businessUrl = n.BusinessUrl == null || n.BusinessUrl == "" ? v.BusinessUrl : n.BusinessUrl,
                            auditUrl = (n.NodeType == (int)WFNodeType.Start || n.NodeType == (int)WFNodeType.End) ? null : (n.AuditUrl == null || n.AuditUrl == "" ? v.AuditUrl : n.AuditUrl),
                            modelKey = n.ModelKey == null || n.ModelKey == "" ? v.ModelKey : n.ModelKey,
                            size = v.Size
                        }).FirstOrDefault();

                    if (flow.node == null)
                    {
                        return new WorkFlowInstance() { error = "流程当前节点不存在" };
                    }

                    GetDefNodeProps(db, flow.node);

                    if(!string.IsNullOrEmpty(flow.node.before))
                    {
                        WorkFlowBeforeEventContext context = new WorkFlowBeforeEventContext();
                        context.instance = db.Set<WFInstanceEntity>().Where(i=>i.Id == flowId).FirstOrDefault();
                        context.dao = db;
                        context.from = flowNode;
                        context.to = flow.node;
                        context.userId = userId;

                        string error = null;
                        //事件中可以改变节点属性
                        MethodInfo before = WorkFlowHelper.GetEventMethod(context.to.before, BeforeEventArgs, out error);
                        if (error != null)
                        {
                            return new WorkFlowInstance() { error = error };
                        }

                        before.Invoke(null, new object[] { context });
                        if (context.cancel)
                        {
                            return new WorkFlowInstance() { error = string.IsNullOrEmpty(context.message) ? "开始执行事件取消执行": context.message };
                        }

                        if (context.error != null)
                        {
                            return new WorkFlowInstance() { error = string.IsNullOrEmpty(context.message) ? Common.ShowException(context.error) : context.message };
                        }
                    }

                    if(flow.node.properties != null)
                    {
                        WorkFlowNodeProperty prop = flow.node.properties.Where(p => p.type == 0 && p.key == WorkFlowHelper.MODEL_KEYS_KEY).FirstOrDefault();
                        if(prop != null)
                        {
                            flow.node.properties.Remove(prop);
                            if (!string.IsNullOrEmpty(prop.value))
                            {
                                flow.modelKeys = new Dictionary<string, WorkFlowModelKey>();
                                JsonConvert.DeserializeObject<List<WorkFlowModelKey>>(prop.value).ForEach(mk =>
                                {
                                    flow.modelKeys[mk.name] = mk;
                                });
                            }
                        }
                    }

                    if(flow.node.type == (int)WFNodeType.Start)
                    {
                        //开始节点添加任务属性，可以编辑表单
                        flow.node.type |= (int)WFNodeType.Task;
                    }

                    return flow;
                }
            }
        }

        #endregion

        #region Private Methods
        /// <summary>
        /// 过滤属性
        /// </summary>
        /// <param name="nodeId">节点ID</param>
        /// <param name="source">源列表</param>
        /// <param name="filters"></param>
        /// <returns></returns>
        private List<WFInstanceNodePropertyEntity> FilterFlowProperty(long nodeId, List<WorkFlowNodeProperty> source, 
            List<WorkFlowNodeProperty> filter)
        {
            if(source == null || source.Count == 0)
            {
                return new List<WFInstanceNodePropertyEntity>();
            }

            Dictionary<string, WFInstanceNodePropertyEntity> dic = new Dictionary<string, WFInstanceNodePropertyEntity>();
            foreach(WorkFlowNodeProperty prop in source)
            {
                if(filter != null && filter.Any(p=>p.type == prop.type 
                && p.key == prop.key 
                && Common.IsEqual(p.value,prop.value)))
                {
                    continue;
                }

                string key = $"{prop.type}:{prop.key}";
                if(dic.Equals(key))
                {
                    if(!string.IsNullOrEmpty(prop.name) && string.IsNullOrEmpty(dic[key].Name))
                    {
                        dic[key].Name = prop.name;
                    }

                    dic[key].Value = prop.value;
                }
                else
                {
                    dic[key] = new WFInstanceNodePropertyEntity()
                    {
                        NodeId = nodeId,
                        Key = prop.key,
                        Value = prop.value ?? string.Empty,
                        Name = prop.name ?? string.Empty,
                        PropType = prop.type
                    };
                }
            }
            return dic.Values.ToList();
        }

        /// <summary>
        /// 过滤属性
        /// </summary>
        /// <param name="nodeId">节点ID</param>
        /// <param name="source">源列表</param>
        /// <param name="filters"></param>
        /// <returns></returns>
        private List<WorkFlowNodeProperty> DistinctProperty(List<WorkFlowNodeProperty> source)
        {
            if (source == null || source.Count == 0)
            {
                return new List<WorkFlowNodeProperty>();
            }

            Dictionary<string, WorkFlowNodeProperty> dic = new Dictionary<string, WorkFlowNodeProperty>();
            foreach (WorkFlowNodeProperty prop in source)
            {
                if (string.IsNullOrEmpty(prop.key))
                {
                    continue;
                }

                string key = $"{prop.type}:{prop.key}";
                if (dic.Equals(key))
                {
                    if (!string.IsNullOrEmpty(prop.name) && string.IsNullOrEmpty(dic[key].name))
                    {
                        dic[key].name = prop.name;
                    }

                    dic[key].value = prop.value;
                }
                else
                {
                    dic[key] = prop;
                }
            }
            return dic.Values.ToList();
        }

        /// <summary>
        /// 获取指定类型下所有用户
        /// </summary>
        /// <param name="db">数据库连接</param>
        /// <param name="userType">1用户，2角色，3部门，4岗位</param>
        /// <param name="orgs">组织ID，部门，岗位，角色</param>
        /// <param name="ids">ID过滤</param>
        /// <returns></returns>
        private List<string> GetAllUser(DbContext db, int userType, string[] orgs, List<string> ids = null)
        {
            if(orgs.Length < 4)
            {
                throw new ArgumentOutOfRangeException(nameof(orgs),"当前登陆用户组织信息不全");
            }

            string orgId = orgs[0];

            //同组织
            IQueryable<SysUserEntity> query = db.Set<SysUserEntity>().Where(u => u.F_OrganizeId == orgId
            && u.F_EnabledMark);
            switch (userType)
            {
                case 1:
                    if (ids != null)
                    {
                        query = query.Where(u => ids.Contains(u.F_Id));
                    }

                    return query.Select(u => u.F_Id).ToList();
                case 2:
                    IQueryable<SysRoleEntity> role = db.Set<SysRoleEntity>().Where(r=>r.F_OrganizeId == orgId 
                    && r.F_EnabledMark);

                    if (ids != null)
                    {
                        role = role.Where(r => ids.Contains(r.F_Id));
                    }
                    return query.Join(role, u => u.F_RoleId, r => r.F_Id,
                    (u, r) => u.F_Id).ToList();
                case 3:
                    IQueryable<SysOrganizeEntity> depart = db.Set<SysOrganizeEntity>().Where(o => o.F_ParentId == orgId
                    && o.F_EnabledMark);

                    if (ids != null)
                    {
                        depart = depart.Where(o => ids.Contains(o.F_Id));
                    }

                    return query.Join(depart, u => u.F_DepartmentId, o => o.F_Id,
                    (u, o) => u.F_Id).ToList();
                case 4:
                    IQueryable<SysRoleEntity> post = db.Set<SysRoleEntity>().Where(p => p.F_OrganizeId == orgId && p.F_CategoryId == 2
                    && p.F_EnabledMark);

                    if (ids != null)
                    {
                        post = post.Where(p => ids.Contains(p.F_Id));
                    }
                    return query.Join(post, u => u.F_DutyId, p => p.F_Id,
                    (u, p) => u.F_Id).ToList();
            }

            return new List<string>();
        }

        /// <summary>
        /// 设置流程分类状态
        /// </summary>
        /// <param name="parents">父级分类列表</param>
        /// <param name="state">状态</param>
        /// <param name="userId">操作用户ID</param>
        /// <param name="db">数据库连接</param>
        /// <param name="repo">数据仓库</param>
        private void DeleteChildCategory(List<WFCategoryEntity> parents, bool state, string userId,
        DbContext db, IWFCategoryRepository repo)
        {
            //Dictionary<int,bool> ps = parents.ToDictionary(p=>p.Id,p=>p.F_DeleteMark);
            List<int> pids = parents.Select(p => p.Id).ToList();
            List<WFCategoryEntity> list = db.Set<WFCategoryEntity>()
            .Where(c => pids.Contains(c.ParentId) && c.F_DeleteMark != state)
            .Select(c => new {
                c.Id,
                c.ParentId,
                c.F_DeleteMark
            }).ToList()
            .Select(c => new WFCategoryEntity()
            {
                Id = c.Id,
                F_DeleteMark = state,
                F_LastUpdateTime = DateTime.Now,
                F_LastUpdateUser = userId
            }).ToList();

            if (list.Count > 0)
            {
                repo.Update(db, DeleteItemColumns, list);
                DeleteChildCategory(list, state, userId, db, repo);
            }
        }

        /// <summary>
        /// 新增分类
        /// </summary>
        /// <param name="model">分类信息</param>
        /// <returns>新增结果</returns>
        private EditItemResult AddCategory(WorkFlowCategory model)
        {
            using (var scope = OperatorProvider.BeginLifetimeScope(null))
            {
                using (var db = scope.Resolve<DbContext>())
                {
                    EditItemResult result = CheckAddCategory(db, model);
                    if (result != null)
                    {
                        return result;
                    }

                    WFCategoryEntity entity = new WFCategoryEntity();
                    entity.Code = model.code;
                    entity.Name = model.name;
                    entity.ParentId = model.parentId;
                    entity.Remark = model.remark;
                    entity.Level = model.level;
                    entity.SortIdx = model.sortIdx;
                    entity.F_DeleteMark = false;
                    entity.F_CreateUser = model.createUser;
                    entity.F_CreateTime = DateTime.Now;
                    entity.F_OrganizeId = model.orgId;

                    IWFCategoryRepository repos = OperatorProvider.Resolve<IWFCategoryRepository>();
                    int count = repos.Insert(db, entity);
                    if (count > 0)
                    {
                        return EditItemResult.Ok(null, entity.Id.ToString());
                    }
                    else
                    {
                        return EditItemResult.Fail("创建失败");
                    }
                }
            }
        }

        /// <summary>
        /// 检查新增分类必要参数
        /// </summary>
        /// <param name="model">分类信息</param>
        /// <returns>校验结果</returns>
        private EditItemResult CheckAddCategory(DbContext db, WorkFlowCategory model)
        {
            if (string.IsNullOrWhiteSpace(model.code))
            {
                return EditItemResult.Fail("分类编码不能为空");
            }

            if (string.IsNullOrEmpty(model.name))
            {
                return EditItemResult.Fail("分类名称不能为空");
            }

            model.level = 1;

            if (model.parentId > 0)
            {
                var pcate = db.Set<WFCategoryEntity>().Where(c => c.Id == model.parentId).Select(c => new { c.Id, c.Level }).FirstOrDefault();
                if (pcate == null)
                {
                    return EditItemResult.Fail($"父级分类({model.parentId})不存在");
                }

                model.level = pcate.Level + 1;
            }

            if (db.Set<WFCategoryEntity>().Any(c => c.Code == model.code))
            {
                return EditItemResult.Fail($"分类编码({model.code})已存在");
            }
            return null;
        }

        /// <summary>
        /// 更新流程分类
        /// </summary>
        /// <param name="model">流程分类</param>
        /// <returns>操作结果</returns>
        private EditItemResult UpdateCategory(WorkFlowCategory model)
        {
            using (var scope = OperatorProvider.BeginLifetimeScope(null))
            {
                using (var db = scope.Resolve<DbContext>())
                {
                    WFCategoryEntity entity = db.Set<WFCategoryEntity>().FirstOrDefault(i => i.Id == model.id);
                    if (entity == null)
                    {
                        return EditItemResult.Fail("该分类已不存在");
                    }

                    List<string> updateColumns = null;
                    EditItemResult result = CheckUpdateCategory(db, model, entity, out updateColumns);
                    if (result != null)
                    {
                        return result;
                    }

                    IWFCategoryRepository repos = OperatorProvider.Resolve<IWFCategoryRepository>();

                    int count = repos.Update(db, updateColumns, entity);
                    if (count > 0)
                    {
                        return EditItemResult.Ok(null, count.ToString());
                    }
                    else
                    {
                        return EditItemResult.Fail("更新失败");
                    }
                }
            }
        }

        /// <summary>
        /// 校验修改分类信息
        /// </summary>
        /// <param name="db">数据库访问实例</param>
        /// <param name="model">分类信息</param>
        /// <param name="entity">分类实体信息</param>
        /// <param name="updateColumns">需要更新的列</param>
        /// <returns>校验结果</returns>
        private EditItemResult CheckUpdateCategory(DbContext db,
            WorkFlowCategory model,
            WFCategoryEntity entity,
            out List<string> updateColumns)
        {
            updateColumns = new List<string>();
            if (!string.IsNullOrWhiteSpace(model.code)
            && !entity.Code.Equals(model.code, StringComparison.OrdinalIgnoreCase))
            {
                entity.Code = model.code;
                updateColumns.Add("Code");

                if (db.Set<WFCategoryEntity>().Any(c => c.Code == model.code && c.Id != model.id))
                {
                    return EditItemResult.Fail("分类编码不能为空");
                }
            }

            if (!string.IsNullOrWhiteSpace(model.name)
            && !entity.Name.Equals(model.name, StringComparison.OrdinalIgnoreCase))
            {
                entity.Name = model.name;
                updateColumns.Add("Name");
            }

            entity.Remark = model.remark;
            updateColumns.Add("Remark");

            if (model.parentId != entity.ParentId)
            {
                entity.ParentId = model.parentId;
                updateColumns.Add("ParentId");
                updateColumns.Add("Level");

                entity.Level = 1;
                if (entity.ParentId > 0)
                {
                    var pcate = db.Set<WFCategoryEntity>()
                        .Where(c => c.Id == entity.ParentId)
                        .Select(c => new { c.Id, c.Level }).FirstOrDefault();
                    if (pcate == null)
                    {
                        return EditItemResult.Fail($"父级分类({model.parentId})不存在");
                    }

                    entity.Level = pcate.Level + 1;
                }
            }

            entity.F_LastUpdateTime = DateTime.Now;
            entity.F_LastUpdateUser = model.lastUpdateUser;
            updateColumns.Add("F_LastUpdateTime");
            updateColumns.Add("F_LastUpdateUser");

            return null;
        }

        /// <summary>
        /// 发送流程处理信息
        /// </summary>
        /// <param name="flow">流程</param>
        /// <param name="nodeIds">处理的节点ID</param>
        private Dictionary<string, int> SendFlowMsg(WFInstanceEntity flow, string nodeIds, string userId)
        {
            //节点用户
            Dictionary<string, int> nodeUsers = new Dictionary<string, int>();

            if (flow == null
                || string.IsNullOrEmpty(nodeIds)
                || string.IsNullOrEmpty(userId))
            {
                return nodeUsers;
            }

            using (var scope = OperatorProvider.BeginLifetimeScope(null))
            {
                using (var db = scope.Resolve<DbContext>())
                {
                    var org = db.Set<SysUserEntity>().Where(u => u.F_Id == userId).Select(u => new { 
                        u.F_OrganizeId,
                        u.F_DepartmentId,
                        u.F_DutyId,
                        u.F_RoleId
                    }).FirstOrDefault();
                    if (org == null || string.IsNullOrEmpty(org.F_OrganizeId))
                    {
                        return nodeUsers;
                    }

                    string[] orgs = { org.F_OrganizeId, org.F_DepartmentId, org.F_DutyId, org.F_RoleId };
                    org = null;

                    DbContextTransaction trans = db.Database.BeginTransaction();
                    IWFMessageRepository msgRepos = OperatorProvider.Resolve<IWFMessageRepository>();
                    string[] ids = nodeIds.Split(',');
                    List<WFMessageEntity> list = new List<WFMessageEntity>();
                    List<WorkFlowNodeUser> users = null;
                    WorkFlowNode node = null;
                    Dictionary<int, List<string>> allUsers = new Dictionary<int, List<string>>();
                    foreach (string id in ids)
                    {
                        #region 节点处理消息
                        string nodeId = id;
                        int idx = id.IndexOf(":");
                        if (idx > -1)
                        {
                            nodeId = id.Substring(idx + 1);
                        }

                        node = db.Set<WFNodeEntity>().Where(n => n.Id == nodeId).Select(n => new WorkFlowNode
                        {
                            id = n.Id,
                            type = n.NodeType,
                            name = n.Name
                        }).FirstOrDefault();

                        if (node == null)
                        {
                            nodeUsers[id] = 0;
                            continue;
                        }

                        List<string> userIds = null;
                        //开始和结束节点只给发起人
                        if (node.type == (int)WFNodeType.Start || node.type == (int)WFNodeType.End)
                        {
                            userIds = new List<string> { flow.F_CreateUser };
                        }
                        else
                        {
                            users = db.Set<WFNodeEntity>().Where(n => n.Id == nodeId)
                                .Join(db.Set<WFNodeUserEntity>(), n => n.Id, u => u.NodeId, (n, u) => new WorkFlowNodeUser
                                {
                                    userId = u.UserId,
                                    userType = u.UserType,
                                    isCandidate = u.IsCandidate
                                }).ToList();

                            //没有配置用户，全部用户
                            if (users.Count == 0)
                            {
                                if (!allUsers.ContainsKey(1))
                                {
                                    allUsers[1] = GetAllUser(db, 1, orgs);
                                }
                                userIds = allUsers[1];
                            }
                            else
                            {
                                userIds = GetNodeUserIds(db, orgs, users, false, allUsers);
                                if (userIds.Count == 0)
                                {
                                    //候选人只选择1个
                                    userIds = GetNodeUserIds(db, orgs, users, true, allUsers);
                                    if (userIds.Count > 1)
                                    {
                                        userIds = userIds.Take(1).ToList();
                                    }
                                }
                            }
                        }

                        nodeUsers[id] = userIds.Count;
                        if (userIds.Count > 0)
                        {
                            list.AddRange(userIds.Select(ui => new WFMessageEntity()
                            {
                                Id = Guid.NewGuid().ToString(),
                                InstanceId = flow.Id,
                                NodeId = id,
                                Msg = string.IsNullOrEmpty(node.name) ? (node.type == 1 ? "开始流程" : (node.type == 2 ? "结束流程" : "待处理")) : node.name,
                                OperateMan = ui,
                                F_LastUpdateTime = DateTime.Now,
                                State = 0,
                                Weight = 0
                            }));

                            if (list.Count > 500)
                            {
                                int offset = 0;
                                while (offset < list.Count)
                                {
                                    msgRepos.Insert(db, list.Skip(offset).Take(500).ToList());
                                    offset += 500;
                                }
                                list.Clear();
                            }
                        }
                        #endregion
                    }

                    if (list.Count > 0)
                    {
                        msgRepos.Insert(db, list);
                    }

                    trans.Commit();
                }
            }

            return nodeUsers;
        }

        /// <summary>
        /// 删除流程节点消息
        /// </summary>
        /// <param name="db">数据库连接</param>
        /// <param name="flowId">流程实例ID</param>
        /// <param name="nodeIds">多个节点</param>
        /// <param name="remark">备注</param>
        private void DeleteFlowMsgs(DbContext db, string flowId, string[] nodeIds, string remark)
        {
            if (db == null || string.IsNullOrEmpty(flowId) || nodeIds == null || nodeIds.Length == 0)
            {
                return;
            }

            foreach (string id in nodeIds)
            {
                DeleteFlowMsg(db, flowId, id, remark);
            }
        }

        /// <summary>
        /// 删除流程节点消息
        /// </summary>
        /// <param name="db">数据库连接</param>
        /// <param name="flowId">流程实例ID</param>
        /// <param name="nodeId">节点ID</param>
        /// <param name="remark">备注</param>
        private void DeleteFlowMsg(DbContext db, string flowId, string nodeId, string remark)
        {
            if (db == null || string.IsNullOrEmpty(flowId) || string.IsNullOrEmpty(nodeId))
            {
                return;
            }

            IWFMessageRepository msgRepos = OperatorProvider.Resolve<IWFMessageRepository>();
            List<WFMessageEntity> msgs = db.Set<WFMessageEntity>().Where(m => m.InstanceId == flowId
            && m.NodeId == nodeId && m.State == 0).ToList();
            if (msgs.Count > 0)
            {
                msgs.ForEach(m => {
                    m.Remark = remark;
                    m.State = 2;
                    m.F_LastUpdateTime = DateTime.Now;
                });

                int offset = 0;
                while (offset < msgs.Count)
                {
                    msgRepos.Update(db, DeleteMsgColumns, msgs.Skip(offset).Take(500).ToList());
                    offset += 500;
                }
            }
        }

        /// <summary>
        /// 更新流程节点为已处理
        /// </summary>
        /// <param name="db">数据库连接</param>
        /// <param name="flowId">流程实例ID</param>
        /// <param name="nodeId">节点ID</param>
        /// <param name="userId">用户ID</param>
        /// <param name="remark">备注</param>
        private void UpdateFlowMsg(DbContext db, string flowId, string nodeId,
            string userId, string remark)
        {
            if (db == null
                || string.IsNullOrEmpty(flowId)
                || string.IsNullOrEmpty(nodeId)
                || string.IsNullOrEmpty(userId))
            {
                return;
            }

            List<WFMessageEntity> msgs = db.Set<WFMessageEntity>().Where(m => m.InstanceId == flowId
            && m.NodeId == nodeId && m.State == 0 && m.OperateMan != userId).ToList();
            if (msgs.Count > 0)
            {
                msgs.ForEach(m => {
                    m.Remark = "删除非本人消息";
                    m.State = 2;
                    m.F_LastUpdateTime = DateTime.Now;
                });
            }

            WFMessageEntity msg = db.Set<WFMessageEntity>().Where(m => m.InstanceId == flowId
            && m.NodeId == nodeId && m.State == 0 && m.OperateMan == userId).FirstOrDefault();
            if (msg != null)
            {
                msg.State = 1;
                msg.F_LastUpdateTime = DateTime.Now;
                msg.Remark = remark;
                msgs.Add(msg);
            }

            if (msgs.Count > 0)
            {
                IWFMessageRepository msgRepos = OperatorProvider.Resolve<IWFMessageRepository>();
                int offset = 0;
                while (offset < msgs.Count)
                {
                    msgRepos.Update(db, DeleteMsgColumns, msgs.Skip(offset).Take(500).ToList());
                    offset += 500;
                }
            }
        }

        /// <summary>
        /// 校验事件有效性
        /// </summary>
        /// <param name="name">定义节点名称</param>
        /// <param name="validKeys">校验列表</param>
        /// <param name="keyPrefix">键前缀</param>
        /// <param name="eventType">事件类型</param>
        /// <param name="eventName">事件全称</param>
        /// <param name="args">事件方法参数</param>
        /// <returns></returns>
        private EditItemResult CheckFlowDefEvent(List<string> validKeys, string name,
            string keyPrefix, string eventType, string eventName, Type[] args)
        {
            string key = eventName;
            key = key.Trim().Replace(" ", "");
            if (key.IndexOf('.') == -1)
            {
                //[WorkFlowEngine.]StaticMethod
                key = "DJY.Application.WorkFlow.WorkFlowEngine." + key;
            }

            if (key.IndexOf(",") == -1)
            {
                //Class.StaticMethod
                if (key.IndexOf('.') == key.LastIndexOf('.'))
                {
                    key = "DJY.Application." + key;
                }

                key += ",DJY.Application";
            }

            string ckey = keyPrefix + key;
            if (!validKeys.Contains(ckey))
            {
                string[] keys = key.Split(',');
                int idx = keys[0].LastIndexOf(".");
                string method = keys[0].Substring(idx + 1);
                keys[0] = keys[0].Substring(0, idx);
                try
                {
                    Type type = Type.GetType(string.Join(",", keys));
                    if (type == null)
                    {
                        return EditItemResult.Fail($"{name}{eventType}事件类型({eventName})未找到");
                    }

                    MethodInfo mi = type.GetMethod(method, args);
                    if (mi == null)
                    {
                        return EditItemResult.Fail($"{name}{eventType}事件方法({method})未找到");
                    }

                    if (!mi.IsStatic)
                    {
                        return EditItemResult.Fail($"{name}{eventType}事件({method})不是静态方法");
                    }
                }
                catch
                {
                    return EditItemResult.Fail($"{name}{eventType}事件类型({eventName})未找到");
                }

                validKeys.Add(ckey);
            }
            return null;
        }

        /// <summary>
        /// 流程实例流转
        /// </summary>
        /// <param name="db">数据库连接</param>
        /// <param name="id">流程实例ID</param>
        /// <returns>查询结果</returns>
        private List<WorkFlowInstanceNode> FlowNodeList(DbContext db, string id)
        {
            List<WorkFlowInstanceNode> nodes = db.Set<WFInstanceNodeEntity>().Where(n => n.InstanceId == id)
                .Join(db.Set<SysUserEntity>(),
                    n => n.F_CreateUser, u => u.F_Id, (n, u) => new
                    {
                        n.Id,
                        n.FromNode,
                        n.FromNodeName,
                        n.NodeId,
                        n.NodeName,
                        n.NodeType,
                        n.InstanceId,
                        n.F_CreateTime,
                        n.F_CreateUser,
                        n.Remark,
                        F_CreateUserName = u.F_RealName == null ? u.F_NickName : u.F_RealName,
                    }).ToList().Select(n => new WorkFlowInstanceNode()
                    {
                        ID = n.Id,
                        id = n.Id.ToString(),
                        instanceId = n.InstanceId,
                        fromNode = n.FromNode?.ToString(),
                        fromNodeName = n.FromNodeName,
                        nodeId = n.NodeId,
                        nodeType = n.NodeType,
                        nodeName = n.NodeName,
                        createTime = n.F_CreateTime.ToString("yyyy-MM-dd HH:mm:ss"),
                        remark = n.Remark,
                        createUser = n.F_CreateUserName
                    }).ToList();

            List<long> nodeIds = nodes.Select(n => n.ID).ToList();
            if (nodeIds.Count > 0)
            {
                List<WFInstanceNodePropertyEntity> props = db.Set<WFInstanceNodePropertyEntity>().Where(p => nodeIds.Contains(p.NodeId)).ToList();
                nodes.ForEach(n =>
                {
                    n.properties = props.Where(p => p.NodeId == n.ID).Select(p => new WorkFlowNodeProperty()
                    {
                        name = p.Name,
                        key = p.Key,
                        value = p.Value,
                        type = p.PropType
                    }).ToList();
                });
            }
            return nodes;
        }

        /// <summary>
        /// 无页面流程执行下一步，页面需要通过“我的待办”处理
        /// </summary>
        /// <param name="context">执行上下文</param>
        /// <param name="path">执行路径，防止死循环</param>
        private void Next(WorkFlowBeforeEventContext context, StringBuilder path)
        {
            context.error = null;
            context.cancel = false;
            context.message = null;
            if (context.from == null && context.to != null)
            {
                if (context.to.type != 1)
                {
                    throw new ArgumentException("流程入口必须是开始节点");
                }

                #region 开始流程
                //填充节点属性
                GetDefNodeProps(context.dao, context.to);
                AddFlowNodeResult(context.to,true);
                //保存业务数据，验证
                if (!string.IsNullOrEmpty(context.to.before))
                {
                    string error = null;
                    //事件中可以改变节点属性
                    MethodInfo before = WorkFlowHelper.GetEventMethod(context.to.before, BeforeEventArgs, out error);
                    if (error != null)
                    {
                        throw new ArgumentException(error);
                    }

                    before.Invoke(null, new object[] { context });
                    if (context.cancel)
                    {
                        throw new OperationCanceledException(context.message);
                    }

                    if (context.error != null)
                    {
                        throw context.error;
                    }
                }

                WFInstanceNodeEntity iNode = new WFInstanceNodeEntity();
                iNode.FromNode = null;
                iNode.FromNodeName = null;
                iNode.NodeType = context.to.type;
                iNode.NodeId = context.to.id;
                iNode.NodeName = string.IsNullOrEmpty(context.to.name) ? "开始流程" : context.to.name;
                iNode.InstanceId = context.instance.Id;
                iNode.F_CreateTime = DateTime.Now;
                iNode.F_CreateUser = context.userId;
                iNode.Remark = "自动完成";

                IWFInstanceNodeRepository nodeRepos = OperatorProvider.Resolve<IWFInstanceNodeRepository>();
                nodeRepos.Insert(context.dao, iNode);

                WorkFlowInstanceNode from = new WorkFlowInstanceNode();
                from.instanceId = iNode.InstanceId;
                from.createTime = iNode.F_CreateTime.ToString("yyyy-MM-dd HH:mm:ss");
                from.createUser = iNode.F_CreateUser;
                from.fromNode = iNode.FromNode?.ToString();
                from.fromNodeName = iNode.FromNodeName;
                from.nodeId = iNode.NodeId;
                from.nodeType = iNode.NodeType;
                from.nodeName = context.to.name;
                from.after = context.to.after;
                from.modelKey = context.to.modelKey;
                from.ID = iNode.Id;
                from.id = iNode.Id.ToString();

                GetFlowNodeProps(context.dao,from,1);
                List<WFInstanceNodePropertyEntity> props = FilterFlowProperty(iNode.Id, context.to.properties, from.properties); ;
                if (props.Count > 0)
                {
                    IWFInstanceNodePropertyRepository pRepos = OperatorProvider.Resolve<IWFInstanceNodePropertyRepository>();
                    pRepos.Insert(context.dao, props);
                }
                from.properties.AddRange(context.to.properties.Where(p => p.type == 0));
                #endregion
                //开始节点
                path.Append("/" + context.to.id);
                if (IsDeadLoop(path.ToString(), context.to.id))
                {
                    throw new InvalidOperationException($"节点{context.to.name}存在死循环");
                }
                context.from = from;
                context.to = null;
                ExecuteAfterEvent(context, from.after, from.nodeName);
                Next(context, path);
            }
            else if (context.from != null && context.to == null)
            {
                context.instance = context.dao.Set<WFInstanceEntity>().Where(i => i.Id == context.instance.Id).FirstOrDefault();
                if (context.instance == null)
                {
                    throw new InvalidOperationException("流程不存在");
                }

                if (string.IsNullOrEmpty(context.instance.NodeId))
                {
                    throw new InvalidOperationException("流程已结束");
                }

                string fromNodeId = string.IsNullOrEmpty(context.from.fromNode) ? context.from.nodeId : $"{context.from.fromNode}:{context.from.nodeId}";
                string[] nodeIds = context.instance.NodeId.Split(',');
                if (!nodeIds.Contains(fromNodeId))
                {
                    throw new InvalidOperationException("当前节点不在待执行列表中");
                }

                IWFInstanceRepository flowRepos = OperatorProvider.Resolve<IWFInstanceRepository>();

                context.instance.F_LastUpdateTime = DateTime.Now;
                context.instance.F_LastUpdateUser = context.userId;

                if ((context.from.nodeType & (int)WFNodeType.Countersign) == (int)WFNodeType.Countersign)
                {
                    //会签节点
                    if (nodeIds.Length > 1)
                    {
                        UpdateFlowMsg(context.dao, context.instance.Id, fromNodeId, context.userId, "会签");
                        //移除当前节点
                        context.instance.Remark = "会签";
                        context.instance.NodeId = string.Join(",", nodeIds.Where(n => !n.Equals(fromNodeId)));
                        flowRepos.Update(context.dao, UpdateFlowColumns, context.instance);
                        return;
                    }
                }

                //当前节点完成
                UpdateFlowMsg(context.dao, context.instance.Id, fromNodeId, context.userId, "节点完成");
                string[] dNodes = nodeIds.Where(n => !n.Equals(fromNodeId)).ToArray();
                if (dNodes.Length > 0)
                {
                    DeleteFlowMsgs(context.dao, context.instance.Id, dNodes, "删除兄弟节点消息");
                }

                Type modelType = null;
                if(!string.IsNullOrEmpty(context.from.modelKey))
                {
                    string error = null;
                    modelType = WorkFlowHelper.GetModelType(context.from.modelKey, out error);
                    if (error != null)
                    {
                        throw new ArgumentException(error);
                    }
                }

                //中间过程切换,排除备注节点
                ExpandoObject obj = new ExpandoObject();
                if (context.from.properties != null)
                {
                    WorkFlowNodeProperty.ConvertTo(context.from.properties, obj as IDictionary<string, object>, modelType);
                }

                #region 计算下一个节点
                List<WFNodeLinkEntity> links = context.dao.Set<WFNodeLinkEntity>().Where(nl => nl.WFID == context.instance.WFID
                && nl.StartNode == context.from.nodeId).Join(context.dao.Set<WFNodeEntity>().Where(n => n.NodeType != (int)WFNodeType.Comment),
                nl => nl.EndNode, n => n.Id, (nl, n) => nl).ToList().Where(nl => string.IsNullOrWhiteSpace(nl.Condition) || nl.Condition.Eval<bool>(obj))
                .ToList();

                if (links.Count > 1)
                {
                    context.instance.Remark = context.from.nodeName + "多路流转";
                    context.instance.NodeId = string.Join(",", links.Select(nl => $"{context.from.id}:{nl.EndNode}"));

                    flowRepos.Update(context.dao, UpdateFlowColumns, context.instance);

                    Dictionary<string, int> nodeUsers = SendFlowMsg(context.instance, context.instance.NodeId, context.userId);
                    //没有操作用户的自动跳过
                    foreach (KeyValuePair<string, int> kv in nodeUsers.Where(kv => kv.Value == 0))
                    {
                        SkipNode(context, kv.Key, path);
                    }
                }
                if (links.Count == 1)
                {
                    string toNodeId = links[0].EndNode;
                    WorkFlowNode to = context.dao.Set<WFNodeEntity>().Where(n => n.Id == toNodeId)
                        .Join(context.dao.Set<WFVersionEntity>(), n => new WorkFlowVersionKey { Id = n.WFID, Version = n.Version },
                        v => new WorkFlowVersionKey { Id = v.WFID, Version = v.Version },
                        (n, v) => new WorkFlowNode
                        {
                            id = n.Id,
                            name = n.Name,
                            type = n.NodeType,
                            code = n.Code,
                            remark = n.Remark,
                            comment = n.Comment,
                            businessUrl = n.NodeType == (int)WFNodeType.End ? null : (n.BusinessUrl == null || n.BusinessUrl == "" ? v.BusinessUrl : n.BusinessUrl),
                            auditUrl = (n.NodeType == (int)WFNodeType.Start || n.NodeType == (int)WFNodeType.End) ? null : (n.AuditUrl == null || n.AuditUrl == "" ? v.AuditUrl : n.AuditUrl),
                            modelKey = n.ModelKey == null || n.ModelKey == "" ? v.ModelKey : n.ModelKey,
                            before = n.Before,
                            after = n.After,
                        }).FirstOrDefault();

                    if (to == null)
                    {
                        throw new ArgumentNullException($"流程[{context.instance.Name}]节点({toNodeId})不存在");
                    }
                    //下一个节点
                    context.instance.Remark = to.name;
                    context.instance.NodeId = $"{context.from.id}:{toNodeId}";
                    flowRepos.Update(context.dao, UpdateFlowColumns, context.instance);
                    context.to = to;
                    Next(context, path);
                }
                else if (links.Count == 0)
                {
                    context.instance.Remark = "流程结束";
                    context.instance.NodeId = null;
                    flowRepos.Update(context.dao, UpdateFlowColumns, context.instance);

                    ExecuteAfterEvent(context, context.after, null);
                }
                #endregion
            }
            else if (context.from != null && context.to != null)
            {
                context.instance = context.dao.Set<WFInstanceEntity>().Where(i => i.Id == context.instance.Id).FirstOrDefault();
                if (context.instance == null)
                {
                    throw new InvalidOperationException("流程不存在");
                }

                if (string.IsNullOrEmpty(context.instance.NodeId))
                {
                    throw new InvalidOperationException("流程已结束");
                }
                string nodeId = $"{context.from.id}:{context.to.id}";
                string[] nodeIds = context.instance.NodeId.Split(',');
                if (!nodeIds.Contains(nodeId))
                {
                    //当前节点不在待执行列表中
                    throw new InvalidOperationException("当前节点不在待执行列表中");
                }

                IWFInstanceRepository flowRepos = OperatorProvider.Resolve<IWFInstanceRepository>();

                context.instance.F_LastUpdateTime = DateTime.Now;
                context.instance.F_LastUpdateUser = context.userId;

                if ((context.from.nodeType & (int)WFNodeType.Countersign) == (int)WFNodeType.Countersign)
                {
                    //会签节点
                    if (nodeIds.Length > 1)
                    {
                        UpdateFlowMsg(context.dao, context.instance.Id, nodeId, context.userId, "会签");
                        //移除当前节点
                        context.instance.Remark = "会签";
                        context.instance.NodeId = string.Join(",", nodeIds.Where(n => !n.Equals(nodeId)));
                        flowRepos.Update(context.dao, UpdateFlowColumns, context.instance);
                        return;
                    }
                }

                if (string.IsNullOrEmpty(context.to.businessUrl)
                    && string.IsNullOrEmpty(context.to.auditUrl))
                {
                    #region 处理节点
                    //填充节点属性
                    GetDefNodeProps(context.dao, context.to);

                    WFInstanceNodeEntity iNode = new WFInstanceNodeEntity();
                    iNode.FromNode = context.from.ID;
                    iNode.FromNodeName = context.from.nodeName;
                    iNode.NodeType = context.to.type;
                    iNode.NodeId = context.to.id;
                    iNode.NodeName = string.IsNullOrEmpty(context.to.name) ? (context.to.type == (int)WFNodeType.Start ? "开始流程" : (context.to.type == (int)WFNodeType.End ? "结束流程" : "自动处理")) : context.to.name;
                    iNode.InstanceId = context.instance.Id;
                    iNode.F_CreateTime = DateTime.Now;
                    iNode.F_CreateUser = context.userId;
                    iNode.Remark = AddFlowNodeResult(context.to,true);

                    //开始
                    if (!string.IsNullOrEmpty(context.to.before))
                    {
                        string error = null;
                        //事件中可以改变节点属性
                        MethodInfo before = WorkFlowHelper.GetEventMethod(context.to.before, BeforeEventArgs, out error);
                        if (error != null)
                        {
                            throw new ArgumentException(error);
                        }

                        before.Invoke(null, new object[] { context });

                        //流转中开始事件
                        if (context.cancel)
                        {
                            iNode.Remark = SetFlowNodeResult(context.to, false) + "," + (string.IsNullOrEmpty(context.message)?"取消执行": context.message);
                        }
                        else if (context.error != null)
                        {
                            iNode.Remark = SetFlowNodeResult(context.to, false) + "," + (string.IsNullOrEmpty(context.message) ? Common.ShowException(context.error) : context.message);
                        }
                    }

                    IWFInstanceNodeRepository nodeRepos = OperatorProvider.Resolve<IWFInstanceNodeRepository>();
                    nodeRepos.Insert(context.dao, iNode);

                    WorkFlowInstanceNode from = new WorkFlowInstanceNode();
                    from.instanceId = iNode.InstanceId;
                    from.createTime = iNode.F_CreateTime.ToString("yyyy-MM-dd HH:mm:ss");
                    from.createUser = iNode.F_CreateUser;
                    from.fromNode = iNode.FromNode?.ToString();
                    from.fromNodeName = context.from.nodeName;
                    from.nodeId = iNode.NodeId;
                    from.nodeType = iNode.NodeType;
                    from.nodeName = context.to.name;
                    from.modelKey = context.to.modelKey;
                    from.after = context.to.after;
                    from.ID = iNode.Id;
                    from.id = iNode.Id.ToString();
                    
                    GetFlowNodeProps(context.dao,from,1);
                    List<WFInstanceNodePropertyEntity> props = FilterFlowProperty(iNode.Id, context.to.properties, from.properties);
                    if (props.Count > 0)
                    {
                        IWFInstanceNodePropertyRepository pRepos = OperatorProvider.Resolve<IWFInstanceNodePropertyRepository>();
                        pRepos.Insert(context.dao, props);
                    }

                    from.properties.AddRange(context.to.properties.Where(p=>p.type == 0));
                    #endregion

                    //开始节点
                    path.Append("/" + context.to.id);
                    if (IsDeadLoop(path.ToString(), context.to.id))
                    {
                        throw new InvalidOperationException($"节点{context.to.name}存在死循环");
                    }

                    context.from = from;
                    context.to = null;
                    //结束事件收集属性
                    ExecuteAfterEvent(context, from.after, from.nodeName);
                    Next(context, path);
                }
                else
                {
                    Dictionary<string, int> nodeUsers = SendFlowMsg(context.instance, nodeId, context.userId);
                    //没有操作用户的自动跳过
                    foreach (KeyValuePair<string, int> kv in nodeUsers.Where(kv => kv.Value == 0))
                    {
                        SkipNode(context, kv.Key, path);
                    }
                }
            }
        }

        /// <summary>
        /// 没有处理人自动跳过节点
        /// </summary>
        /// <param name="context">处理上下文</param>
        /// <param name="nodeId">节点ID</param>
        /// <param name="path">处理路径</param>
        private void SkipNode(WorkFlowBeforeEventContext context, string nodeId, StringBuilder path)
        {
            if (context == null
                || context.instance == null
                || string.IsNullOrEmpty(nodeId)
                || path == null)
            {
                return;
            }

            //流程会转向
            if (string.IsNullOrEmpty(context.instance.NodeId))
            {
                return;
            }

            string[] nids = context.instance.NodeId.Split(',');
            if (!nids.Contains(nodeId))
            {
                return;
            }

            string fromNode = null;
            string defNode = nodeId;
            int idx = defNode.IndexOf(":");
            if (idx > -1)
            {
                fromNode = defNode.Substring(0, idx);
                defNode = defNode.Substring(idx + 1);
            }

            long? fromNodeId = null;
            WFInstanceNodeEntity flowNode = null;
            if (!string.IsNullOrEmpty(fromNode))
            {
                fromNodeId = long.Parse(fromNode);
                flowNode = context.dao.Set<WFInstanceNodeEntity>().Where(fn => fn.Id == fromNodeId.Value).FirstOrDefault();
            }

            WorkFlowNode node = context.dao.Set<WFNodeEntity>().Where(n => n.Id == defNode)
                .Join(context.dao.Set<WFVersionEntity>(), n => new WorkFlowVersionKey { Id = n.WFID, Version = n.Version },
                v => new WorkFlowVersionKey { Id = v.WFID, Version = v.Version }, (n, v) => new WorkFlowNode
                {
                    id = n.Id,
                    code = n.Code,
                    type = n.NodeType,
                    name = n.Name,
                    remark = n.Remark,
                    rectangle = n.Rectangle,
                    comment = n.Comment,
                    businessUrl = n.NodeType == (int)WFNodeType.End ? null : (n.BusinessUrl == null || n.BusinessUrl == "" ? v.BusinessUrl : n.BusinessUrl),
                    auditUrl = (n.NodeType == (int)WFNodeType.Start || n.NodeType == (int)WFNodeType.End) ? null : (n.AuditUrl == null || n.AuditUrl == "" ? v.AuditUrl : n.AuditUrl),
                    modelKey = n.ModelKey == null || n.ModelKey == "" ? v.ModelKey : n.ModelKey,
                    before = n.Before,
                    after = n.After,
                    skipEvent = v.SkipEvent
                }).FirstOrDefault();

            if (node == null)
            {
                return;
            }

            context.to = node;

            //填充节点属性
            GetDefNodeProps(context.dao, context.to);
            AddFlowNodeResult(context.to, true);
            //跳过事件
            if (!string.IsNullOrEmpty(context.to.skipEvent))
            {
                string error = null;
                //事件中可以改变节点属性
                MethodInfo before = WorkFlowHelper.GetEventMethod(context.to.skipEvent, BeforeEventArgs, out error);
                if (error != null)
                {
                    //配置错误 抛异常
                    throw new ArgumentException(error);
                }

                before.Invoke(null, new object[] { context });
                if (context.cancel)
                {
                    throw new OperationCanceledException(context.message);
                }

                if (context.error != null)
                {
                    throw context.error;
                }
            }
            //开始
            if (!string.IsNullOrEmpty(context.to.before))
            {
                string error = null;
                //事件中可以改变节点属性
                MethodInfo before = WorkFlowHelper.GetEventMethod(context.to.before, BeforeEventArgs, out error);
                if (error != null)
                {
                    //配置错误 抛异常
                    throw new ArgumentException(error);
                }

                before.Invoke(null, new object[] { context });
                if (context.cancel)
                {
                    throw new OperationCanceledException(context.message);
                }

                if (context.error != null)
                {
                    throw context.error;
                }
            }
            //开始
            if (!string.IsNullOrEmpty(context.to.before))
            {
                string error = null;
                //事件中可以改变节点属性
                MethodInfo before = WorkFlowHelper.GetEventMethod(context.to.before, BeforeEventArgs, out error);
                if (error != null)
                {
                    //配置错误 抛异常
                    throw new ArgumentException(error);
                }

                before.Invoke(null, new object[] { context });
                if (context.cancel)
                {
                    throw new OperationCanceledException(context.message);
                }

                if (context.error != null)
                {
                    throw context.error;
                }
            }

            WFInstanceNodeEntity iNode = new WFInstanceNodeEntity();
            iNode.FromNode = fromNodeId;
            iNode.FromNodeName = flowNode?.NodeName;
            iNode.NodeId = defNode;
            iNode.NodeName = string.IsNullOrEmpty(node.name) ? (node.type == (int)WFNodeType.Start ? "开始流程" : node.type == (int)WFNodeType.End ? "结束流程" : "自动跳过") : node.name;
            iNode.NodeType = node.type;
            iNode.InstanceId = context.instance.Id;
            iNode.F_CreateTime = DateTime.Now;
            iNode.F_CreateUser = context.userId;
            iNode.Remark = "未匹配人员自动跳过";

            IWFInstanceNodeRepository nodeRepos = OperatorProvider.Resolve<IWFInstanceNodeRepository>();
            nodeRepos.Insert(context.dao, iNode);

            WorkFlowInstanceNode from = new WorkFlowInstanceNode();
            from.instanceId = iNode.InstanceId;
            from.createTime = iNode.F_CreateTime.ToString("yyyy-MM-dd HH:mm:ss");
            from.createUser = iNode.F_CreateUser;
            from.fromNode = iNode.FromNode?.ToString();
            from.fromNodeName = iNode.FromNodeName;
            from.nodeId = iNode.NodeId;
            from.nodeType = iNode.NodeType;
            from.nodeName = iNode.NodeName;
            from.modelKey = node.modelKey;
            from.after = node.after;
            from.ID = iNode.Id;
            from.id = iNode.Id.ToString();

            GetFlowNodeProps(context.dao, from, 1);
            List<WFInstanceNodePropertyEntity> props = FilterFlowProperty(iNode.Id, context.to.properties, from.properties);
            if (props.Count > 0)
            {
                IWFInstanceNodePropertyRepository pRepos = OperatorProvider.Resolve<IWFInstanceNodePropertyRepository>();
                pRepos.Insert(context.dao, props);
            }
            from.properties.AddRange(context.to.properties.Where(p=>p.type == 0));

            path.Append("/" + context.to.id);
            if (IsDeadLoop(path.ToString(), context.to.id))
            {
                throw new InvalidOperationException($"节点{context.to.name}存在死循环");
            }

            context.from = from;
            context.to = null;
            //结束事件收集属性
            ExecuteAfterEvent(context,from.after,from.nodeName);
            Next(context, path);
        }

        /// <summary>
        /// 判断是否是死循环
        /// </summary>
        /// <param name="path">路径</param>
        /// <param name="key">键</param>
        /// <returns></returns>
        private bool IsDeadLoop(string path, string key)
        {
            if (string.IsNullOrEmpty(path) || string.IsNullOrEmpty(key))
            {
                return false;
            }

            string[] keys = path.Split(new string[] { key }, 4, StringSplitOptions.None);
            //至少出现2次，在第3次时发现
            if (keys.Length < 4 || keys[1] == null || keys[1] == string.Empty)
            {
                return false;
            }

            return keys[1] == keys[2];

        }

        /// <summary>
        /// 获取流程定义节点属性
        /// </summary>
        /// <param name="db">数据库连接</param>
        /// <param name="node">节点</param>
        private void GetDefNodeProps(DbContext db, WorkFlowNode node)
        {
            node.properties = db.Set<WFNodePropertyEntity>().Where(p => p.NodeId == node.id)
                .Select(p => new WorkFlowNodeProperty()
                {
                    nodeId = node.id,
                    key = p.Key,
                    name = p.Name,
                    value = p.Value,
                    type = p.PropType
                }).ToList();
        }

        /// <summary>
        /// 获取流程实例节点属性
        /// </summary>
        /// <param name="db">数据库连接</param>
        /// <param name="node">节点</param>
        /// <param name="propType">属性类型-1 全部 0 属性，1数据</param>
        private void GetFlowNodeProps(DbContext db, WorkFlowInstanceNode node,int propType = -1)
        {
            //节点属性覆盖数据
            IQueryable<WorkFlowNodeProperty> query = null;
            if (propType == -1)
            {
                //全部
                query = db.Set<WFInstanceNodePropertyEntity>().Where(p => p.PropType == 1)
                .Join(db.Set<WFInstanceNodeEntity>().Where(n=>n.InstanceId == node.instanceId), p => p.NodeId, n => n.Id, (p, n) => new WorkFlowNodeProperty
                {
                    nodeId = node.id,
                    key = p.Key,
                    name = p.Name,
                    value = p.Value,
                    type = p.PropType
                }).Concat(db.Set<WFInstanceNodePropertyEntity>().Where(p => p.PropType == 0 && p.NodeId == node.ID)
                .Select(p => new WorkFlowNodeProperty
                {
                    nodeId = node.id,
                    key = p.Key,
                    name = p.Name,
                    value = p.Value,
                    type = p.PropType
                }));
            }
            else if (propType == 0)
            {
                //属性
                query = db.Set<WFInstanceNodePropertyEntity>().Where(p => p.PropType == 0 && p.NodeId == node.ID)
                .Select(p => new WorkFlowNodeProperty
                {
                    nodeId = node.id,
                    key = p.Key,
                    name = p.Name,
                    value = p.Value,
                    type = p.PropType
                });
            }
            else
            {
                //数据合并
                query = db.Set<WFInstanceNodePropertyEntity>().Where(p => p.PropType == propType)
                .Join(db.Set<WFInstanceNodeEntity>().Where(n => n.InstanceId == node.instanceId), p => p.NodeId, n => n.Id, 
                (p, n) => new WorkFlowNodeProperty
                {
                    nodeId = node.id,
                    key = p.Key,
                    name = p.Name,
                    value = p.Value,
                    type = p.PropType
                });
            }
            node.properties = query.ToList();
        }

        /// <summary>
        /// 添加节点结果
        /// </summary>
        /// <param name="node"></param>
        /// <param name="ok"></param>
        /// <returns></returns>
        private string AddFlowNodeResult(WorkFlowNode node, bool ok)
        {
            if (node == null)
            {
                throw new ArgumentNullException(nameof(node));
            }

            if(node.properties == null)
            {
                node.properties = new List<WorkFlowNodeProperty>();
            }

            WorkFlowNodeProperty prop = node.properties.FirstOrDefault(p => p.key.Equals(WorkFlowHelper.NODE_RESULT_KEY));
            if (prop == null)
            {
                prop = new WorkFlowNodeProperty()
                {
                    nodeId = node.id,
                    key = WorkFlowHelper.NODE_RESULT_KEY,
                    name = "处理结果",
                    type = 0,
                    value = ok ? WorkFlowHelper.NODE_OK_RESULT : WorkFlowHelper.NODE_FAIL_RESULT
                };
                node.properties.Add(prop);
            }

            string result = prop.value == WorkFlowHelper.NODE_OK_RESULT ? "处理完成" : "处理失败";

            if ((node.type & (int)WFNodeType.Audit) == (int)WFNodeType.Audit)
            {
                prop = node.properties.FirstOrDefault(p => p.key.Equals(WorkFlowHelper.NODE_AUDIT_KEY));
                if (prop == null)
                {
                    prop = new WorkFlowNodeProperty()
                    {
                        nodeId = node.id,
                        key = WorkFlowHelper.NODE_AUDIT_KEY,
                        name = "审核结果",
                        type = 0,
                        value = ok ? WorkFlowHelper.NODE_OK_AUDIT : WorkFlowHelper.NODE_FAIL_AUDIT
                    };
                    node.properties.Add(prop);
                }

                result = prop.value == WorkFlowHelper.NODE_OK_AUDIT ? "审核通过" : "审核失败";
            }

            return result;
        }

        /// <summary>
        /// 添加节点结果
        /// </summary>
        /// <param name="node"></param>
        /// <param name="ok"></param>
        /// <returns></returns>
        private string SetFlowNodeResult(WorkFlowNode node, bool ok)
        {
            if (node == null)
            {
                throw new ArgumentNullException(nameof(node));
            }

            if (node.properties == null)
            {
                node.properties = new List<WorkFlowNodeProperty>();
            }

            WorkFlowNodeProperty prop = node.properties.FirstOrDefault(p => p.key.Equals(WorkFlowHelper.NODE_RESULT_KEY));
            if (prop == null)
            {
                prop = new WorkFlowNodeProperty()
                {
                    nodeId = node.id,
                    key = WorkFlowHelper.NODE_RESULT_KEY,
                    name = "处理结果",
                    type = 0,
                    value = ok ? WorkFlowHelper.NODE_OK_RESULT : WorkFlowHelper.NODE_FAIL_RESULT
                };
                node.properties.Add(prop);
            }
            else
            {
                prop.value = ok ? WorkFlowHelper.NODE_OK_RESULT : WorkFlowHelper.NODE_FAIL_RESULT;
            }

            string result = ok ? "处理成功" : "处理失败";

            if ((node.type & (int)WFNodeType.Audit) == (int)WFNodeType.Audit)
            {
                prop = node.properties.FirstOrDefault(p => p.key.Equals(WorkFlowHelper.NODE_AUDIT_KEY));
                if (prop == null)
                {
                    prop = new WorkFlowNodeProperty()
                    {
                        nodeId = node.id,
                        key = WorkFlowHelper.NODE_AUDIT_KEY,
                        name = "审核结果",
                        type = 0,
                        value = ok ? WorkFlowHelper.NODE_OK_AUDIT : WorkFlowHelper.NODE_FAIL_AUDIT
                    };
                    node.properties.Add(prop);
                }
                else
                {
                    prop.value = ok ? WorkFlowHelper.NODE_OK_AUDIT : WorkFlowHelper.NODE_FAIL_AUDIT;
                }

                result = ok ? "审核通过" : "审核失败";
            }

            return result;
        }

        /// <summary>
        /// 校验工作流定义是否有效
        /// </summary>
        /// <param name="def">工作流定义</param>
        /// <returns></returns>
        private EditItemResult CheckWorkFlowDef(DbContext db,WorkFlowDef def)
        {
            if(string.IsNullOrEmpty(def.name))
            {
                return EditItemResult.Fail("流程定义名称不能为空");
            }

            var cate = db.Set<WFCategoryEntity>()
                .Where(c => c.Id == def.cateId).Select(c => new { c.Id, c.Name, c.F_DeleteMark })
                .FirstOrDefault();
            if (cate == null)
            {
                return EditItemResult.Fail($"流程分类({def.cateId})不存在");
            }

            //新增
            if (string.IsNullOrEmpty(def.id) && cate.F_DeleteMark)
            {
                return EditItemResult.Fail($"流程分类({def.cateId})不可用");
            }

            if (string.IsNullOrEmpty(def.id) && (def.nodes == null || def.nodes.Count == 0))
            {
                return EditItemResult.Fail("新增流程未设置节点");
            }

            if (def.nodes != null && def.nodes.Count > 0)
            {
                if (def.nodeLinks == null || def.nodeLinks.Count == 0)
                {
                    return EditItemResult.Fail("流程没有流转");
                }

                List<string> ids = def.nodes.Select(n => n.id).ToList();
                //不忽略大小写
                if (def.nodeLinks.Any(nl => !ids.Contains(nl.startNode) || !ids.Contains(nl.endNode)))
                {
                    return EditItemResult.Fail("流程存在无效流转节点");
                }

                if (def.nodeLinks.Any(nl => nl.startNode.Equals(nl.endNode,StringComparison.OrdinalIgnoreCase) 
                && string.IsNullOrEmpty(nl.condition)))
                {
                    return EditItemResult.Fail("自流转节点没有流转条件");
                }

                if (def.nodeLinks.Any(nl=>string.IsNullOrEmpty(nl.startPosition) || string.IsNullOrEmpty(nl.endPosition)))
                {
                    return EditItemResult.Fail("流程存在无效流转");
                }

                if (!def.nodes.Any(n => n.type == (int)WFNodeType.Start))
                {
                    return EditItemResult.Fail("流程无开始节点");
                }
                if (!def.nodes.Any(n => n.type == (int)WFNodeType.End))
                {
                    return EditItemResult.Fail("流程无结束节点");
                }

                if(def.nodes.Any(n=>n.type == (int)WFNodeType.Start && !def.nodeLinks.Any(nl=>nl.startNode == n.id)))
                {
                    return EditItemResult.Fail("开始节点不能是孤立节点");
                }

                if (def.nodes.Any(n => n.type != (int)WFNodeType.Start
                && n.type != (int)WFNodeType.Comment //备注节点可以是孤立节点
                && !def.nodeLinks.Any(nl => nl.endNode == n.id)))
                {
                    return EditItemResult.Fail("流程中存在孤立节点");
                }
            }
            return null;
        }

        /// <summary>
        /// 添加流程定义
        /// </summary>
        /// <param name="db">数据库访问</param>
        /// <param name="def">工作流定义</param>
        /// <returns></returns>
        private EditItemResult AddWorkFlowDef(DbContext db, WorkFlowDef def)
        {
            DbContextTransaction trans = db.Database.BeginTransaction();
            try
            {
                def.id = Guid.NewGuid().ToString();
                IWFDefRepository defRepos = OperatorProvider.Resolve<IWFDefRepository>();
                defRepos.Insert(db, new WFDefEntity()
                {
                    Id = def.id,
                    CateId = def.cateId,
                    Name = def.name,
                    Remark = def.remark,
                    Version = 1,
                    F_DeleteMark = false,
                    F_CreateTime = DateTime.Now,
                    F_CreateUser = def.createUser,
                    StableVersion = 0,
                    F_OrganizeId = def.orgId
                });

                //流程版本
                IWFVersionRepository verRepos = OperatorProvider.Resolve<IWFVersionRepository>();
                verRepos.Insert(db, new WFVersionEntity()
                {
                    WFID = def.id,
                    Version = 1,
                    Remark = def.remark,
                    F_DeleteMark = false,
                    F_CreateTime = DateTime.Now,
                    F_CreateUser = def.createUser,
                    Before = def.before,
                    After = def.after,
                    SkipEvent = def.skipEvent,
                    Size = def.size,
                    BusinessUrl = def.businessUrl ?? string.Empty,
                    AuditUrl = def.auditUrl ?? string.Empty,
                    ModelKey = def.modelKey ?? string.Empty
                });

                //节点扩展属性和数据
                List<WFNodePropertyEntity> props = new List<WFNodePropertyEntity>();
                //节点绑定用户
                List<WFNodeUserEntity> users = new List<WFNodeUserEntity>();
                //节点信息
                List<WFNodeEntity> nodes = def.nodes.Select(n => {
                    WFNodeEntity node = new WFNodeEntity() { 
                        Id = n.id,
                        WFID = def.id,
                        Version = 1,
                        NodeType = n.type,
                        Name = n.name ?? string.Empty,
                        Remark = n.remark,
                        Rectangle = n.rectangle??string.Empty,
                        Comment = n.comment,
                        BusinessUrl = n.businessUrl,
                        AuditUrl = n.auditUrl,
                        ModelKey = n.modelKey,
                        F_DeleteMark = false,
                        F_CreateTime = DateTime.Now,
                        F_CreateUser = def.createUser,
                        Before = n.before,
                        After = n.after,
                        Code = n.code
                    };

                    if(n.users != null && n.users.Count>0)
                    {
                        users.AddRange(n.users.Select(nu => new WFNodeUserEntity() { 
                            WFID = def.id,
                            NodeId = n.id,
                            UserId = nu.userId,
                            UserType = nu.userType,
                            IsCandidate = nu.isCandidate,
                            UserName = nu.userName
                        }));
                    }

                    if(n.properties != null && n.properties.Count>0)
                    {
                        Dictionary<string, WFNodePropertyEntity> nps = new Dictionary<string, WFNodePropertyEntity>();
                        n.properties.ForEach(np=> {
                            if (!string.IsNullOrWhiteSpace(np.key))
                            {
                                string key = $"{np.key}.{np.type}";
                                if (nps.ContainsKey(key))
                                {
                                    //覆盖属性
                                    if (!string.IsNullOrEmpty(np.name))
                                    {
                                        nps[key].Name = np.name;
                                    }

                                    if (!string.IsNullOrEmpty(np.value))
                                    {
                                        nps[key].Value = np.value;
                                    }
                                }
                                else
                                {
                                    nps[key] = new WFNodePropertyEntity()
                                    {
                                        WFID = def.id,
                                        NodeId = n.id,
                                        Key = np.key,
                                        Name = np.name ?? string.Empty,
                                        Value = np.value,
                                        PropType = np.type
                                    };
                                }
                            }
                        });

                        if(nps.Count>0)
                        {
                            props.AddRange(nps.Values);
                        }
                    }

                    return node;
                }).ToList();

                IWFNodeRepository nodeRepos = OperatorProvider.Resolve<IWFNodeRepository>();
                nodeRepos.Insert(db,nodes);

                if (users.Count > 0)
                {
                    IWFNodeUserRepository userRepos = OperatorProvider.Resolve<IWFNodeUserRepository>();
                    userRepos.Insert(db, users);
                }

                if (props.Count > 0)
                {
                    IWFNodePropertyRepository propRepos = OperatorProvider.Resolve<IWFNodePropertyRepository>();
                    propRepos.Insert(db, props);
                }

                List<WFNodeLinkEntity> links = def.nodeLinks.Select(nl => new WFNodeLinkEntity() {
                    WFID = def.id,
                    Id = Guid.NewGuid().ToString(),
                    Text = nl.text,
                    TextPosition = nl.textPosition,
                    LinkType = 0,
                    StartNode = nl.startNode,
                    EndNode = nl.endNode,
                    Condition = nl.condition,
                    F_CreateTime = DateTime.Now,
                    F_CreateUser = def.createUser,
                    StartPosition = nl.startPosition ?? string.Empty,
                    EndPosition = nl.endPosition
                }).ToList();

                IWFNodeLinkRepository linkRepos = OperatorProvider.Resolve<IWFNodeLinkRepository>();
                linkRepos.Insert(db, links);

                trans.Commit();

                return EditItemResult.Ok(null, def.id);
            }
            catch (Exception ex)
            {
                try
                {
                    trans.Rollback();
                }
                catch
                {
                    //回滚失败
                }
                return EditItemResult.Fail(Common.ShowException(ex));
            }
        }

        /// <summary>
        /// 更新工作流定义
        /// </summary>
        /// <param name="db">数据库连接</param>
        /// <param name="def">工作流定义</param>
        /// <returns></returns>
        private EditItemResult UpdateWorkFlowDef(DbContext db, WorkFlowDef def)
        {
            WFDefEntity flow = db.Set<WFDefEntity>().FirstOrDefault(d => d.Id == def.id);
            if (flow == null)
            {
                return EditItemResult.Fail("指定流程不存在");
            }

            WFVersionEntity flowVer = db.Set<WFVersionEntity>().FirstOrDefault(d => d.Version == flow.Version && d.WFID == def.id);
            if (flowVer == null)
            {
                return EditItemResult.Fail("指定流程不存在");
            }

            //更新操作
            if (def.version == 0 || def.version == flow.Version)
            {
                return UpdateWorkFlowDef(flow, flowVer, db, def);
            }
            else
            {
                //创建新版本
                return WorkFlowNewVersion(flow, flowVer, db, def);
            }
        }

        /// <summary>
        /// 更新工作流定义
        /// </summary>
        /// <param name="flow">流程定义</param>
        /// <param name="flowVer">流程最新版本信息</param>
        /// <param name="db">数据库连接</param>
        /// <param name="def">工作流定义</param>
        /// <returns></returns>
        private EditItemResult UpdateWorkFlowDef(WFDefEntity flow, WFVersionEntity flowVer, 
            DbContext db, WorkFlowDef def)
        {
            DbContextTransaction trans = db.Database.BeginTransaction();
            int count = 0;
            try
            {
                flow.CateId = def.cateId;
                flow.Name = def.name;
                flow.Remark = def.remark;
                flow.F_LastUpdateTime = DateTime.Now;
                flow.F_LastUpdateUser = def.lastUpdateUser;

                IWFDefRepository defRepos = OperatorProvider.Resolve<IWFDefRepository>();
                count = defRepos.Update(db, UpdateDefColumns, flow);

                flowVer.Before = def.before;
                flowVer.After = def.after;
                flowVer.BusinessUrl = def.businessUrl ?? string.Empty;
                flowVer.AuditUrl = def.auditUrl ?? string.Empty;
                flowVer.ModelKey = def.modelKey ?? string.Empty;
                flowVer.Size = def.size;
                flowVer.SkipEvent = def.skipEvent;

                IWFVersionRepository verRepos = OperatorProvider.Resolve<IWFVersionRepository>();
                verRepos.Update(db, UpdateDefVerColumns, flowVer);

                if(def.nodes != null && def.nodes.Count>0)
                {
                    List<string> nodeIds = db.Set<WFNodeEntity>().Where(n => n.WFID == flow.Id 
                    && n.Version == flow.Version).Select(n => n.Id).ToList();

                    //节点扩展属性和数据
                    List<WFNodePropertyEntity> props = new List<WFNodePropertyEntity>();
                    //节点绑定用户
                    List<WFNodeUserEntity> users = new List<WFNodeUserEntity>();

                    List<WFNodeEntity> updateNodes = new List<WFNodeEntity>();
                    List<WFNodeEntity> addNodes = new List<WFNodeEntity>();
                    def.nodes.ForEach(n => {
                        if(nodeIds.Contains(n.id,StringComparer.OrdinalIgnoreCase))
                        {
                            updateNodes.Add(new WFNodeEntity()
                            {
                                Id = n.id,
                                Name = n.name ?? string.Empty,
                                Remark = n.remark,
                                Rectangle = n.rectangle ?? string.Empty,
                                Comment = n.comment,
                                BusinessUrl = n.businessUrl,
                                AuditUrl = n.auditUrl,
                                ModelKey = n.modelKey,
                                F_LastUpdateUser = def.lastUpdateUser,
                                F_LastUpdateTime = DateTime.Now,
                                Before = n.before,
                                After = n.after,
                                Code = n.code
                            });
                        }
                        else
                        {
                            addNodes.Add(new WFNodeEntity()
                            {
                                Id = n.id,
                                WFID = def.id,
                                Version = 1,
                                NodeType = n.type,
                                Name = n.name ?? string.Empty,
                                Remark = n.remark,
                                Rectangle = n.rectangle ?? string.Empty,
                                Comment = n.comment,
                                BusinessUrl = n.businessUrl,
                                AuditUrl = n.auditUrl,
                                ModelKey = n.modelKey,
                                F_DeleteMark = false,
                                F_CreateTime = DateTime.Now,
                                F_CreateUser = def.createUser,
                                Before = n.before,
                                After = n.after,
                                Code = n.code
                            });
                        }

                        if (n.users != null && n.users.Count > 0)
                        {
                            users.AddRange(n.users.Select(nu => new WFNodeUserEntity()
                            {
                                WFID = def.id,
                                NodeId = n.id,
                                UserId = nu.userId,
                                UserType = nu.userType,
                                IsCandidate = nu.isCandidate,
                                UserName = nu.userName
                            }));
                        }

                        if (n.properties != null && n.properties.Count > 0)
                        {
                            Dictionary<string, WFNodePropertyEntity> nps = new Dictionary<string, WFNodePropertyEntity>();
                            n.properties.ForEach(np => {
                                if (!string.IsNullOrWhiteSpace(np.key))
                                {
                                    string key = $"{np.key}.{np.type}";
                                    if (nps.ContainsKey(key))
                                    {
                                        //覆盖属性
                                        if (!string.IsNullOrEmpty(np.name))
                                        {
                                            nps[key].Name = np.name;
                                        }

                                        if (!string.IsNullOrEmpty(np.value))
                                        {
                                            nps[key].Value = np.value;
                                        }
                                    }
                                    else
                                    {
                                        nps[key] = new WFNodePropertyEntity()
                                        {
                                            WFID = def.id,
                                            NodeId = n.id,
                                            Key = np.key,
                                            Name = np.name,
                                            Value = np.value,
                                            PropType = np.type
                                        };
                                    }
                                }
                            });

                            if (nps.Count > 0)
                            {
                                props.AddRange(nps.Values);
                            }
                        }
                    });

                    IWFNodeRepository nodeRepos = OperatorProvider.Resolve<IWFNodeRepository>();
                    if(updateNodes.Count>0)
                    {
                        nodeRepos.Update(db, UpdateDefNodeColumns, updateNodes);
                    }

                    if(addNodes.Count>0)
                    {
                        nodeRepos.Insert(db, addNodes);
                    }

                    IWFNodeUserRepository userRepos = OperatorProvider.Resolve<IWFNodeUserRepository>();
                    userRepos.Delete(db, u => nodeIds.Contains(u.NodeId));
                    if (users.Count > 0)
                    {
                        userRepos.Insert(db, users);
                    }

                    IWFNodePropertyRepository propRepos = OperatorProvider.Resolve<IWFNodePropertyRepository>();
                    propRepos.Delete(db, p => nodeIds.Contains(p.NodeId));
                    if (props.Count > 0)
                    {
                        propRepos.Insert(db, props);
                    }

                    List<WFNodeLinkEntity> links = def.nodeLinks.Select(nl => new WFNodeLinkEntity()
                    {
                        WFID = def.id,
                        Id = Guid.NewGuid().ToString(),
                        Text = nl.text,
                        TextPosition = nl.textPosition,
                        LinkType = 0,
                        StartNode = nl.startNode,
                        EndNode = nl.endNode,
                        Condition = nl.condition,
                        F_CreateTime = DateTime.Now,
                        F_CreateUser = def.createUser,
                        StartPosition = nl.startPosition ?? string.Empty,
                        EndPosition = nl.endPosition
                    }).ToList();

                    IWFNodeLinkRepository linkRepos = OperatorProvider.Resolve<IWFNodeLinkRepository>();
                    linkRepos.Delete(db, nl => nodeIds.Contains(nl.StartNode) || nodeIds.Contains(nl.EndNode));
                    linkRepos.Insert(db, links);
                }

                trans.Commit();

                return EditItemResult.Ok(null, count.ToString());
            }
            catch (Exception ex)
            {
                try
                {
                    trans.Rollback();
                }
                catch
                {
                    //回滚失败
                }
                return EditItemResult.Fail(Common.ShowException(ex));
            }
        }

        /// <summary>
        /// 更新工作流定义
        /// </summary>
        /// <param name="flow">流程定义</param>
        /// <param name="flowVer">流程最新版本信息</param>
        /// <param name="db">数据库连接</param>
        /// <param name="def">工作流定义</param>
        /// <returns></returns>
        private EditItemResult WorkFlowNewVersion(WFDefEntity flow, WFVersionEntity flowVer,
            DbContext db, WorkFlowDef def)
        {
            if(def.version <= flow.Version)
            {
                return EditItemResult.Fail("更新版本号必须大于最新版本号");
            }

            DbContextTransaction trans = db.Database.BeginTransaction();
            int count = 0;
            try
            {
                flow.CateId = def.cateId;
                flow.Version = def.version;
                flow.Name = def.name;
                flow.Remark = def.remark;
                flow.F_LastUpdateTime = DateTime.Now;
                flow.F_LastUpdateUser = def.lastUpdateUser;

                IWFDefRepository defRepos = OperatorProvider.Resolve<IWFDefRepository>();
                count = defRepos.Update(db, UpdateDefColumns.Concat(VersionColumns).ToArray(), flow);

                //流程版本
                IWFVersionRepository verRepos = OperatorProvider.Resolve<IWFVersionRepository>();
                verRepos.Insert(db, new WFVersionEntity()
                {
                    WFID = def.id,
                    Version = def.version,
                    Remark = def.remark,
                    F_DeleteMark = false,
                    F_CreateTime = DateTime.Now,
                    F_CreateUser = def.createUser,
                    Before = def.before,
                    After = def.after,
                    SkipEvent = def.skipEvent,
                    BusinessUrl = def.businessUrl ?? string.Empty,
                    AuditUrl = def.auditUrl ?? string.Empty,
                    ModelKey = def.modelKey ?? string.Empty,
                    Size = def.size
                });

                Dictionary<string, string> nodeIds = new Dictionary<string, string>();
                //节点扩展属性和数据
                List<WFNodePropertyEntity> props = new List<WFNodePropertyEntity>();
                //节点绑定用户
                List<WFNodeUserEntity> users = new List<WFNodeUserEntity>();
                //节点信息
                List<WFNodeEntity> nodes = def.nodes.Select(n => {

                    //新的节点ID
                    nodeIds[n.id] = Guid.NewGuid().ToString().ToLower();
                    n.id = nodeIds[n.id];

                    WFNodeEntity node = new WFNodeEntity()
                    {
                        Id = n.id,
                        WFID = def.id,
                        Version = def.version,
                        NodeType = n.type,
                        Name = n.name ?? string.Empty,
                        Remark = n.remark,
                        Rectangle = n.rectangle ?? string.Empty,
                        Comment = n.comment,
                        BusinessUrl = n.businessUrl,
                        AuditUrl = n.auditUrl,
                        ModelKey = n.modelKey,
                        F_DeleteMark = false,
                        F_CreateTime = DateTime.Now,
                        F_CreateUser = def.createUser,
                        Before = n.before,
                        After = n.after,
                        Code = n.code
                    };

                    if (n.users != null && n.users.Count > 0)
                    {
                        users.AddRange(n.users.Select(nu => new WFNodeUserEntity()
                        {
                            WFID = def.id,
                            NodeId = n.id,
                            UserId = nu.userId,
                            UserType = nu.userType,
                            IsCandidate = nu.isCandidate,
                            UserName = nu.userName
                        }));
                    }

                    if (n.properties != null && n.properties.Count > 0)
                    {
                        Dictionary<string, WFNodePropertyEntity> nps = new Dictionary<string, WFNodePropertyEntity>();
                        n.properties.ForEach(np => {
                            if (!string.IsNullOrWhiteSpace(np.key))
                            {
                                string key = $"{np.key}.{np.type}";
                                if (nps.ContainsKey(key))
                                {
                                    //覆盖属性
                                    if (!string.IsNullOrEmpty(np.name))
                                    {
                                        nps[key].Name = np.name;
                                    }

                                    if (!string.IsNullOrEmpty(np.value))
                                    {
                                        nps[key].Value = np.value;
                                    }
                                }
                                else
                                {
                                    nps[key] = new WFNodePropertyEntity()
                                    {
                                        WFID = def.id,
                                        NodeId = n.id,
                                        Key = np.key,
                                        Name = np.name,
                                        Value = np.value,
                                        PropType = np.type
                                    };
                                }
                            }
                        });

                        if (nps.Count > 0)
                        {
                            props.AddRange(nps.Values);
                        }
                    }

                    return node;
                }).ToList();

                IWFNodeRepository nodeRepos = OperatorProvider.Resolve<IWFNodeRepository>();
                nodeRepos.Insert(db, nodes);

                if (users.Count > 0)
                {
                    IWFNodeUserRepository userRepos = OperatorProvider.Resolve<IWFNodeUserRepository>();
                    userRepos.Insert(db, users);
                }

                if (props.Count > 0)
                {
                    IWFNodePropertyRepository propRepos = OperatorProvider.Resolve<IWFNodePropertyRepository>();
                    propRepos.Insert(db, props);
                }

                List<WFNodeLinkEntity> links = def.nodeLinks.Select(nl => new WFNodeLinkEntity()
                {
                    WFID = def.id,
                    Id = Guid.NewGuid().ToString(),
                    Text = nl.text,
                    TextPosition = nl.textPosition,
                    LinkType = 0,
                    StartNode = nodeIds[nl.startNode],
                    EndNode = nodeIds[nl.endNode],
                    Condition = nl.condition,
                    F_CreateTime = DateTime.Now,
                    F_CreateUser = def.createUser,
                    StartPosition = nl.startPosition ?? string.Empty,
                    EndPosition = nl.endPosition
                }).ToList();

                IWFNodeLinkRepository linkRepos = OperatorProvider.Resolve<IWFNodeLinkRepository>();
                linkRepos.Insert(db, links);

                trans.Commit();

                return EditItemResult.Ok(null, count.ToString());
            }
            catch (Exception ex)
            {
                try
                {
                    trans.Rollback();
                }
                catch
                {
                    //回滚失败
                }
                return EditItemResult.Fail(Common.ShowException(ex));
            }
        }

        /// <summary>
        /// 执行结束事件
        /// </summary>
        /// <param name="context">流程上下文</param>
        /// <param name="eventName">事件名称</param>
        /// <param name="nodeName">流程节点名称</param>
        private void ExecuteAfterEvent(WorkFlowBeforeEventContext context, 
            string eventName, string nodeName)
        {
            if (context == null
                || context.instance == null
                || string.IsNullOrEmpty(eventName)
                || string.IsNullOrEmpty(context.userId))
                return;

            string log = $"({context.instance.Id}:{context.instance.Name})结束事件：{{0}}";
            if(!string.IsNullOrEmpty(nodeName))
            {
                log = $"({context.instance.Id}:{context.instance.Name}){nodeName}，结束事件：{{0}}";
            }

            //处理结束事件
            string error = null;
            MethodInfo after = WorkFlowHelper.GetEventMethod(eventName, AfterEventArgs, out error);
            if (error != null)
            {
                WriteLog(false, string.Format(log, error), context.userId);
            }
            else
            {
                after.Invoke(null, new object[] { context });
            }
        }

        /// <summary>
        /// 写日志
        /// </summary>
        /// <param name="result">是否成功</param>
        /// <param name="log">内容</param>
        private void WriteLog(bool result, string log, string userId)
        {
            try
            {
                SysLogEntity logEntity = new SysLogEntity();
                logEntity.F_Id = Guid.NewGuid().ToString();
                logEntity.F_Date = DateTime.Now;
                logEntity.F_Type = DbLogType.WorkFlow.ToString();
                logEntity.F_CreatorTime = DateTime.Now;
                logEntity.F_CreatorUserId = userId;
                logEntity.F_Account = "";
                logEntity.F_Result = result;
                logEntity.F_ModuleName = "工作流";
                logEntity.F_Description = log;

                ISysLogService logService = OperatorProvider.Resolve<ISysLogService>();
                logService.InsertLog(logEntity);
            }
            catch (Exception ex)
            {
                //非线程安全的方法
                Logs.Write(log);
            }
        }

        #endregion
    }
}